- Create a managed class. It's best to explicitly implement an interface to avoid breakage due to versioning down the road.
- Attribute the managed class and its interface(s) with a Guid. In the COM world GUIDs identify coclasses and interfaces.
- Register the managed dll with COM. In visual studio this will take care of exporting the TLB and running regasm on the managed dll.
- 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.
- In C++, get a reference to the interface via CoCreateInstance().
- 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