Wednesday, January 9, 2008

Accessing .NET/C# objects from MFC COM

Turns out that accessing .NET objects from an MFC COM object is relatively straightforward.  The main steps are:
  1. Create a managed class.  It's best to explicitly implement an interface to avoid breakage due to versioning down the road.
  2. Attribute the managed class and its interface(s) with a Guid.  In the COM world GUIDs identify coclasses and interfaces.
  3. Register the managed dll with COM.  In visual studio this will take care of exporting the TLB and running regasm on the managed dll.
  4. To pass a class to an MFC COM object method, define the method as taking a pointer to IUknown.  Every automation object inherits from IUnknown.
  5. In C++, get a reference to the interface via CoCreateInstance().
  6. Call methods/access properties on the interface as needed!

For example, suppose you have a C# class TestClass in TestClass.dll and you want to call TestClass.TestMethod() from an in-process COM server implemented using MFC.  In COM the only way to access the functionality of a class is through an interface.  So you'll need to create an interface containing the methods you want to call and make TestClass inherit from that interface.

C# source:


[Guid("......")]

public interface ITestInterface

{

void TestMethod();

}

[Guid("......."),

ClassInterface(ClassInterfaceType.None)] // to
prevent interop from creating a class interface for you

class TestClass : ITestInterface

{

void TestMethod();

}







To make TestClass and ITestInterface from TestClass.dll visible to C++, the C++ source will need to import both the type library exported from the C#/Managed dll as well as mscorlib.tlb.  To do this, add the following import statements:



C++ .h header file:






#import "mscorlib.tlb"

#import "..\TestClass\bin\Debug\TestClass.tlb" //
created by tlbexp.exe or when visual studio project property "register for COM"
is true










To access TestClass.TestMethod() from C++ you'll need to get a reference to ITestInterface (which TestClass implements).  In COM, interfaces are the only way to access an objects functionality.



C++ .cpp source file (error checking skipped for clarity):






ITestInterface *tptr = NULL;

CoCreateInstance(CLSID_TestClass, NULL,
CLSCTX_INPROC_SERVER, IID_ITestInterface, (void**)(&cpi));

tptr->TestMethod();

tptr->Release(); // COM uses reference counting
to figure out when to release memory










To pass TestClass to an MFC COM object method, you'll have to specify it's parameter type an IUnknown* (aka LPUNKNOWN).



C++ MFC DISPATCH MAP entry:





DISP_FUNCTION_ID(CMyClass, "MyFunc", dispidMyFunc, 
MyFunc, VT_I4, VTS_UNKNOWN) // the vts_unknown ->
IUnknown*










C++ method:






HRESULT CMyClass::MyFunc(IUnknown *pUnk)

{

ITestInterface *pti = NULL; // type
imported from TestClass.tlb


pUnk->QueryInterface(IID_ITestInterface, (void**)
(&pti)); // IID_ITestInterface also defined in
TestClass.tlb

pti->TestFunc(); // calls the
managed method

pti->Release();

}









This method can be called from C# as follows:






TestClass tc = new TestClass();

comObject.SomeFunc(tc); // assumes comObject
is an RCW around the MFC COM object you're passing TestClass to





 

No comments :

Post a Comment