I went through the tutorial of C# COM INterop. Performed the demo example given in the link successfully runs! Understood every statement present there, their meaning. And came across the equivalent data types for c# objects.
I Couldn't find any good books (with examples on C# COM interop and calling COM components from native C++) apart from Adam Nathan's .NET and COM interoperability and Nishant Shivakumar's CCLI in Action. I have no problems in registering assembly and other stuff but facing problems in COM syntax.
Before explaining my code, I wanted to understand what VARIANT is. Why is it there?
This is my situation, I have a C# class as below:
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
namespace ManagedDLL
{
// Interface declaration.
[ComVisible(true), Guid("3265C537-E149-4559-B4E1-DBE334927DFA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalculator
{
int Add(int Number1, int Number2);
int Subtract(Subtraction sb);
};
// Interface implementation.
[ComVisible(true), Guid("0F50D3BE-CEEA-4C57-9882-4A609C7BB36C")]
public class ManagedClass : ICalculator
{
private int a, b;
public int Add(int Number1, int Number2)
{
return Number1 + Number2;
}
public int Subtract(Subtraction sub)
{
int a = 10, b = 3;
return sub.SubtractTwoNumbers(a, b);
}
}
[ComVisible(true)]
[Guid("C718EDDE-541C-4D15-B7EA-0533AB11A839")]
[ClassInterface(ClassInterfaceType.None)]
public class Subtraction
{
public int SubtractTwoNumbers(int a, int b)
{
return a - b;
}
}
}
Got the .tlh file after importing tlb file as below:
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace ManagedDLL {
//
// Forward references and typedefs
//
struct __declspec(uuid("4e5098b7-4e51-45e5-a705-a7e3c51e2a80"))
/* LIBID */ __ManagedDLL;
struct __declspec(uuid("3265c537-e149-4559-b4e1-dbe334927dfa"))
/* interface */ ICalculator;
struct /* coclass */ ManagedClass;
struct /* coclass */ Subtraction;
struct __declspec(uuid("c8e9181c-f064-3ec1-869e-042c6fdd3e46"))
/* dual interface */ _ManagedClass;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(ICalculator, __uuidof(ICalculator));
_COM_SMARTPTR_TYPEDEF(_ManagedClass, __uuidof(_ManagedClass));
//
// Type library items
//
struct __declspec(uuid("3265c537-e149-4559-b4e1-dbe334927dfa"))
ICalculator : IUnknown
{
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall Add (
/*[in]*/ long Number1,
/*[in]*/ long Number2,
/*[out,retval]*/ long * pRetVal ) = 0;
virtual HRESULT __stdcall Subtract (
/*[in]*/ struct _Object * sb,
/*[out,retval]*/ long * pRetVal ) = 0;
};
struct __declspec(uuid("0f50d3be-ceea-4c57-9882-4a609c7bb36c"))
ManagedClass;
// [ default ] interface _ManagedClass
// interface _Object
// interface ICalculator
struct __declspec(uuid("c718edde-541c-4d15-b7ea-0533ab11a839"))
Subtraction;
// [ default ] interface _Object
struct __declspec(uuid("c8e9181c-f064-3ec1-869e-042c6fdd3e46"))
_ManagedClass : IDispatch
{};
//
// Named GUID constants initializations
//
extern "C" const GUID __declspec(selectany) LIBID_ManagedDLL =
{0x4e5098b7,0x4e51,0x45e5,{0xa7,0x05,0xa7,0xe3,0xc5,0x1e,0x2a,0x80}};
extern "C" const GUID __declspec(selectany) IID_ICalculator =
{0x3265c537,0xe149,0x4559,{0xb4,0xe1,0xdb,0xe3,0x34,0x92,0x7d,0xfa}};
extern "C" const GUID __declspec(selectany) CLSID_ManagedClass =
{0x0f50d3be,0xceea,0x4c57,{0x98,0x82,0x4a,0x60,0x9c,0x7b,0xb3,0x6c}};
extern "C" const GUID __declspec(selectany) CLSID_Subtraction =
{0xc718edde,0x541c,0x4d15,{0xb7,0xea,0x05,0x33,0xab,0x11,0xa8,0x39}};
extern "C" const GUID __declspec(selectany) IID__ManagedClass =
{0xc8e9181c,0xf064,0x3ec1,{0x86,0x9e,0x04,0x2c,0x6f,0xdd,0x3e,0x46}};
} // namespace ManagedDLL
#pragma pack(pop)
and finally this is my native C++ application:
HRESULT hr = CoInitialize(NULL);
ManagedDLL::ICalculatorPtr ptrInterface(__uuidof(ManagedDLL::ManagedClass));
IUnknown* pUnk=NULL;
hr=CoCreateInstance(__uuidof(ManagedDLL::Subtraction), NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)pUnk);
long lResult;
ptrInterface->Subtract(pUnk, &lResult); //what should be written instead of pUnk, because it expects a _Object* as evident in the tlh file
return lResult;
Questions galore:
How to access the Subtract method in the interface? How should we instantiate Subtraction class whose object should be passed as parameter in this Subtract method?
How to collect value of a function returning a COM class as return type?
What are IUnknown and IDispatch?
And most importantly, why the _Object* was created as an argument in the COM .tlh file instead of Subtraction* as argument?
The questions you are asking are classical, fundamental questions about COM that could take a small book to answer. I would suggest you get a tutorial or a book on COM that will bring you up to speed. You won't understand the interop until you understand COM basics. Start there, then work through the harder stuff.
It takes some serious time to get up to speed on all the issues involved, so don't get in a hurry!
Here's a place to start: http://msdn.microsoft.com/en-us/library/727z646z(v=vs.80).aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With