com调用的几种方法 及 C#调用C++编写的的COM DLL收藏

时间:2022-08-31 09:07:02

一、COM调用的方法5则:

1,先注册com

Requirement:
1.创建myCom.dll,该COM只有一个组件,两个接口IGetRes--方法Hello(),
   IGetResEx--方法HelloEx()
2.在工程中导入组件或类型库
   #import "组件所在目录myCom.dll" no_namespace
        或
   #import "类型库所在目录myCom.tlb"
   using namespace MYCOM;

--Method 1-------------------------------------------------------
   CoInitialize(NULL);
   CLSID clsid;
   CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
   CComPtr<IGetRes> pGetRes;//智能指针
   pGetRes.CoCreateInstance(clsid);
   pGetRes->Hello();
   pGetRes.Release();//小心哦!!请看最后的“注意”
   CoUninitialize();
--Method 2---------------------------------------------------------
   CoInitialize(NULL);
   CLSID clsid;
   HRESULT hr=CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
   IGetRes *ptr;
   hr=CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,
                 __uuidof(IGetRes),(LPVOID*)&ptr);
   ptr->Hello();
   CoUninitialize();
--Method 3--------------------------------------------------------
   CoInitialize(NULL);
   HRESULT hr;
   CLSID clsid;
   hr=CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
   IGetRes* ptr;
   IGetResEx* ptrEx;

   //使用CoCreateClassObject创建一个组件(特别是mutilThreads)的多个对象的
     时候,效率更高.
   IClassFactory* p_classfactory;
   hr=CoGetClassObject(clsid,CLSCTX_INPROC_SERVER,
                       NULL,IID_IClassFactory,
                       (LPVOID*)&p_classfactory);
   p_classfactory->CreateInstance(NULL,__uuidof(IGetRes),
                                          (LPVOID*)&ptr);
   p_classfactory->CreateInstance(NULL,__uuidof(IGetResEx),
                                          (LPVOID*)&ptrEx);
   ptr->Hello();
   ptrEx->HelloEx();
   CoUninitialize();
--Method 4--------------------------------------------------------
    直接从dll中得到DllGetClassObject,接着生成类对象及类实例(这方法可以
使组件不用在注册表里注册,这是最原始的方法,但这样做没什么意义,至少失去了COM
对用户的透明性),不推荐使用.

typedef HRESULT (__stdcall * pfnHello)(REFCLSID,REFIID,void**);
   pfnHello fnHello= NULL;
   HINSTANCE hdllInst = LoadLibrary("组件所在目录myCom.dll");
   fnHello=(pfnHello)GetProcAddress(hdllInst,"DllGetClassObject");
   if (fnHello != 0)
   {
   IClassFactory* pcf = NULL;
   HRESULT hr=(fnHello)(CLSID_GetRes,IID_IClassFactory,(void**)&pcf);
   if (SUCCEEDED(hr) && (pcf != NULL))
   {
   IGetRes* pGetRes = NULL;
   hr = pcf->CreateInstance(NULL, IID_IFoo, (void**)&pGetRes);
   if (SUCCEEDED(hr)   && (pFoo != NULL))
   {
   pGetRes->Hello();
   pGetRes->Release();
   }
   pcf->Release();
   }
   }
   FreeLibrary(hdllInst);
--Method 5-------------------------------------------------------
    通过ClassWizard利用类型库生成包装类,不过前提是com组件的接口必须是派
生自IDispatch,具体方法:
    调出添加类向导(.NET中),选择类型库中MFC类,打开,选择"文件",选择
"myCom.dll"或"myCom.tlb",接下来会出来该myCom中的所有接口,选择你想
生成的接口包装类后,向导会自动生成相应的.h文件.这样你就可以在你的MFC中
像使用普通类那样使用组件了.(CreateDispatch("myCom.GetRes") 中的参数就是ProgID通过Clsid在注册表中可以查询的到)

   CoInitialize(NULL);
   CGetRes getRest;
   if (getRest.CreateDispatch("myCom.GetRes") != 0)
   {
   getRest.Hello();
   getRest.ReleaseDispatch();
   }
   CoUninitialize();
--注意--------------------------------------------------------------
     COM中的智能指针实际上是重载了->的类,目的是为了简化引用记数,几不需要程序
员显示的调用AddRef()和Release(),但是为什么我们在Method 1中
pGetRes.Release(),问题在与,我们的智能指针pGetRes生命周期的结束是在
CoUninitialize()之后,CoInitialize所开的套间在CoUninitialize()后已经被
关闭,而pGetRes此时发生析构,导致了程序的崩溃,解决这个问题的另一个方法是
   CoInitialize(NULL);
   CLSID clsid;
   CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
   {
   CComPtr<IGetRes> pGetRes;//智能指针
   pGetRes.CoCreateInstance(clsid);
   pGetRes->Hello();
   }
   CoUninitialize();
--------------------------------------------------------------------
以上就是COM的5中方法,当然具体怎么使用还是在于程序的环境,加以琢磨....

 

 

二、C#调用C++编写的的COM DLL

 

在C#调用C++编写的COM DLL封装库时会出现两个问题: 1.  数据类型转换问题 2.  指针或地址参数传送问题       首先是数据类型转换问题。因为C#是.NET语言,利用的是.NET的基本数据类型,所以实际上是将C++的数据类型与.NET的基本数据类型进行对应。       例如C++的原有函数是:   int __stdcall FunctionName(unsigned char param1, unsigned short param2)       其中的参数数据类型在C#中,必须转为对应的数据类型。如:   [DllImport(“ COM DLL path/file ”)] extern static int FunctionName(byte param1, ushort param2)       因为调用的是__stdcall函数,所以使用了P/Invoke的调用方法。其中的方法FunctionName必须声明为静态外部函数,即加上extern static声明头。我们可以看到,在调用的过程中,unsigned char变为了byte,unsigned short变为了ushort。变换后,参数的数据类型不变,只是声明方式必须改为.NET语言的规范。       我们可以通过下表来进行这种转换:  

Win32 Types CLR Type
char, INT8, SBYTE, CHAR  System.SByte
short, short int, INT16, SHORT System.Int16
int, long, long int, INT32, LONG32, BOOL , INT System.Int32
__int64, INT64, LONGLONG System.Int64
unsigned char, UINT8, UCHAR , BYTE System.Byte
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t System.UInt16
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT System.UInt32
unsigned __int64, UINT64, DWORDLONG, ULONGLONG System.UInt64
float, FLOAT System.Single
double, long double, DOUBLE System.Double

      之后再将CLR的数据类型表示方式转换为C#的表示方式。这样一来,函数的参数类型问题就可以解决了。       现在,我们再来考虑下一个问题,如果要调用的函数参数是指针或是地址变量,怎么办?       对于这种情况可以使用C#提供的非安全代码来进行解决,但是,毕竟是非托管代码,垃圾资源处理不好的话对应用程序是很不利的。所以还是使用C#提供的ref以及out修饰字比较好。       同上面一样,我们也举一个例子:   int __stdcall FunctionName(unsigned char &param1, unsigned char *param2)       在C#中对其进行调用的方法是:   [DllImport(“ COM DLL path/file ”)] extern static int FunctionName(ref byte param1, ref byte param2)       看到这,可能有人会问,&是取地址,*是传送指针,为何都只用ref就可以了呢?一种可能的解释是ref是一个具有重载特性的修饰符,会自动识别是取地址还是传送指针。       在实际的情况中,我们利用参数传递地址更多还是用在传送数组首地址上。 如:byte[] param1 = new param1(6);       在这里我们声明了一个数组,现在要将其的首地址传送过去,只要将param1数组的第一个元素用ref修饰。具体如下:   [DllImport(“ COM DLL path/file ”)] extern static int FunctionName(ref byte param1[1], ref byte param2)