C++ COM组件的编写
来源:互联网 发布:河北驰捷网络 编辑:程序博客网 时间:2024/05/22 14:22
COM组件的编写
本文提供一个完全用C++实现的进程内(DLL)COM服务器,不要ATL或MFC提供任何支持。用这种方式编写COM对象可以让你深入地洞察到COM处 理进程内服务器的方法以及COM是如何创建类工厂的。利用本文提供的这个简单框架你可以实现很基本的COM组件,如外壳扩展(Shell Extensions)等。如果你在使用过程中发现了任何问题,请将它反馈到vckbase@public.hk.hi.cn。
以下是用本文所说的方式编写自己的COM对象要经过的步骤:
第一步:写一个头文件,这个头文件包含以下内容:
1、 包含文件comdef.h:
- #include <comdef.h>
2、 定义COM服务器的GUID。
- _declspec(selectany) GUID CLSID_Mine = { 0xdc186800,0x657f,0x11d4,{0xb0, 0xb5, 0x0, 0x50, 0xba, 0xbf, 0xc9, 0x4}};
3、 给出接口的IID以及这个接口要实现的方法定义。到时客户端会用到这个接口的IID和接口的方法。
- interface __declspec(uuid("F614FB00-6702-11d4-B0B7-0050BABFC904")) ImyInterface : public IUnknown
- {
- STDMETHOD(Square)(long *pVal)PURE;
- STDMETHOD(Cube)(long *pVal)PURE;
- };
- HRESULT hr;
- ImyInterface *pmine=(0);
- hr = CoCreateInstance(CLSID_Mine, // COM 服务器的CLSID
- NULL, //不支持聚合
- CLSCTX_INPROC_SERVER, // 是个DLL
- __uuidof(ImyInterface), // 接口的IID
- (void**)&pmine);
还有一种方法可以从注册表中获得COM对象的CLSID,就是调用CLSIDFromProgId()函数,不过必须把组件的ProgId传递给这个函数。
第二步:必须为所定义的接口提供实现,本文用的方法是创建一个从接口继承的新类:
- //
- // 这个类实现单接口ImyInterface ...
- //
- class CmyInterface : public CComBase<> ,public InterfaceImpl<ImyInterface>
- {
- public:
- CmyInterface();
- virtual ~CmyInterface();
- // 我们必须要为QueryInterface 编写代码
- STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv);
- // ImyInterface 接口方法
- STDMETHOD(Square)(long *pVal);
- STDMETHOD(Cube)(long *pVal);
- };
模版类InterfaceImpl<>提供接口引用计数的实现。在此我们可以用多接口继承,那样就能在一个COM组件中实现多个接口。
第三步:在完成这个对象之前,我们还要编写Queryinterface和两个接口方法:
- STDMETHODIMP CmyInterface::QueryInterface(REFIID riid,LPVOID *ppv)
- {
- *ppv = NULL;
- if(IsEqualIID(riid,IID_IUnknown) IsEqualIID(riid,__uuidof(ImyInterface)))
- {
- // 因为我们从ImyInterface继承,所以要进行强制类型转换
- *ppv = (ImyInterface *) this;
- _AddRef(); // 这个方法从某个基类继承而来
- return S_OK;
- }
- return E_NOINTERFACE;
- }
- STDMETHODIMP CmyInterface::Square(long *pVal)
- {
- long value = *pVal;
- *pVal = value * value;
- return S_OK;
- }
- STDMETHODIMP CmyInterface::Cube(long *pVal)
- {
- long value = *pVal;
- *pVal = value * value * value;
- return S_OK;
- }
最后一步:
COM组件的DLLs必须输出一个叫DllGetClassObject的函数。由这个函数为CmyInterface创建类工厂并返回一个对它的引用。然后我们调用CoCreateInstance为进程内COM创建类工厂,接着调用DllGetClassObject。
这个类工厂有一个方法是CreateInstance,由这个方法创建对象并返回对它的引用。
- STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
- {
- *ppvOut = NULL;
- if (IsEqualIID(rclsid, CLSID_Mine))
- {
- // 为CmyInterface类声明类工厂
- CClassFactory<CmyInterface>
- *pcf = new CClassFactory<CmyInterface>;
- return pcf->QueryInterface(riid,ppvOut);
- }
- return CLASS_E_CLASSNOTAVAILABLE;
- }
你可能会问在哪里创建实际的CmyInterface类对象,实际上这是由CClassFactory的模板实例来处理的。以下是CClassFatory的实现:
- // CSingleCreator 用于单实例类工厂,这个类为多个CreateObject请求返回相同的对象指针..
- template<class comObj>
- class CSingleCreator
- {
- protected:
- CSingleCreator():m_pObj(0) {};
- comObj *CreateObject()
- {
- if(!m_pObj)
- {
- m_pObj = new comObj;
- }
- return m_pObj;
- }
- comObj * m_pObj;
- };
- // CMultiCreator 用于常用类工厂,这个类为每一个CreateObject请求返回新的对象指针..
- template<class comObj>
- class CMultiCreator
- {
- protected:
- CMultiCreator():m_pObj(0) {};
- comObj *CreateObject()
- {
- return new comObj;
- }
- comObj * m_pObj;
- };
- //ClassFactory类实现
- // MultiCreator是缺省的类工厂创建者
- //这个类实现了接口IclasFactory......
- class CClassFactory : public CComBase<>,
- public InterfaceImpl<IClassFactory>,
- public creatorClass
- {
- public:
- CClassFactory() {};
- virtual ~CClassFactory() {};
- STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv)
- {
- *ppv = NULL;
- if(IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,IID_IClassFactory))
- {
- *ppv = (IClassFactory *) this;
- _AddRef();
- return S_OK;
- }
- return E_NOINTERFACE;
- }
- STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj)
- {
- *ppvObj = NULL;
- if (pUnkOuter)
- return CLASS_E_NOAGGREGATION;
- m_pObj = CreateObject(); // m_pObj 在creatorClass中定义
- if (!m_pObj)
- return E_OUTOFMEMORY;
- HRESULT hr = m_pObj->QueryInterface(riid, ppvObj);
- if(hr != S_OK)
- {
- delete m_pObj;
- }
- return hr;
- }
- STDMETHODIMP LockServer(BOOL)
- {
- return S_OK; // 未实现
- }
- };
关于代码:本文所提出的方法是如何用纯粹的C++编写COM组件的一个大概念。很多方面的细节都省略了。从本文的文字和代码中可以看出用纯C++编写COM组件需要做些什么工作,如果你要用这种方法编写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中像使用普通类那样使用组件了.
- 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种方法,当然具体怎么使用还是在于程序的环境,加以琢磨....
http://www.yesky.com/dev/382/2019882.shtml
========================================================文二===================================================
本文提供一个完全用C++实现的进程内(DLL)COM服务器,不要ATL或MFC提供任何支持。用这种方式编写COM对象可以让你深入地洞察到COM处理进程内服务器的方法以及COM是如何创建类工厂的。利用本文提供的这个简单框架你可以实现很基本的COM组件,如外壳扩展(Shell Extensions)等。
以下是用本文所说的方式编写自己的COM对象要经过的步骤:
第一步:写一个头文件,这个头文件包含以下内容:
1、 包含文件comdef.h:#include <comdef.h>。
2、 定义COM服务器的GUID。
- _declspec(selectany) GUID CLSID_Mine = { 0xdc186800,
- 0x657f,
- 0x11d4,
- {0xb0, 0xb5, 0x0, 0x50, 0xba, 0xbf, 0xc9, 0x4}
- };
3、 给出接口的IID以及这个接口要实现的方法定义。到时客户端会用到这个接口的IID和接口的方法。
- interface __declspec(uuid("F614FB00-6702-11d4-B0B7-0050BABFC904")) ImyInterface : public IUnknown
- {
- STDMETHOD(Square)(long *pVal)PURE;
- STDMETHOD(Cube)(long *pVal)PURE;
- };
客户端使用此接口:
- HRESULT hr;
- ImyInterface *pmine=(0);
- hr = CoCreateInstance(CLSID_Mine, // COM 服务器的CLSID
- NULL, //不支持聚合
- CLSCTX_INPROC_SERVER, // 是个DLL
- __uuidof(ImyInterface), // 接口的IID
- (void**)&pmine
- );
还有一种方法可以从注册表中获得COM对象的CLSID,就是调用CLSIDFromProgId()函数,不过必须把组件的ProgId传递给这个函数。
第二步:必须为所定义的接口提供实现,本文用的方法是创建一个从接口继承的新类:
- // 这个类实现单接口ImyInterface ...
- //
- //
- class CmyInterface : public CComBase<> ,
- public InterfaceImpl<ImyInterface>
- {
- public:
- CmyInterface();
- virtual ~CmyInterface();
- // 我们必须要为QueryInterface 编写代码
- STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv);
- // ImyInterface 接口方法
- STDMETHOD(Square)(long *pVal);
- STDMETHOD(Cube)(long *pVal);
- };
模版类InterfaceImpl<>提供接口引用计数的实现。在此我们可以用多接口继承,那样就能在一个COM组件中实现多个接口。
第三步:在完成这个对象之前,我们还要编写Queryinterface和两个接口方法:
- STDMETHODIMP CmyInterface::QueryInterface(REFIID riid,LPVOID *ppv)
- {
- *ppv = NULL;
- if(IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,__uuidof(ImyInterface)))
- {
- // 因为我们从ImyInterface继承,所以要进行强制类型转换
- *ppv = (ImyInterface *) this;
- _AddRef(); // 这个方法从某个基类继承而来
- return S_OK;
- }
- return E_NOINTERFACE;
- }
- STDMETHODIMP CmyInterface::Square(long *pVal)
- {
- long value = *pVal;
- *pVal = value * value;
- return S_OK;
- }
- STDMETHODIMP CmyInterface::Cube(long *pVal)
- {
- long value = *pVal;
- *pVal = value * value * value;
- return S_OK;
- }
注意这里使用了__uuidof(ImyInterface)来获取接口的IID,这是因为我们已经在第一步中将这个接口关联到了某个uuid。
最后一步:COM 组件的DLLs必须输出一个叫DllGetClassObject的函数。由这个函数为CmyInterface创建类工厂并返回一个对它的引用。然后我们调用CoCreateInstance为进程内COM创建类工厂,接着调用DllGetClassObject。这个类工厂有一个方法是CreateInstance,由这个方法创建对象并返回对它的引用。
- STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
- {
- *ppvOut = NULL;
- if (IsEqualIID(rclsid, CLSID_Mine))
- {
- // 为CmyInterface类声明类工厂
- CClassFactory<CmyInterface>
- *pcf = new CClassFactory<CmyInterface>;
- return pcf->QueryInterface(riid,ppvOut);
- }
- return CLASS_E_CLASSNOTAVAILABLE;
- }
在此我们要检查所请求的CLSID是不是CLSID_Mine,如果不是则返回一个错误代码。
你可能会问在哪里创建实际的CmyInterface类对象,实际上这是由CClassFactory<CmyInterface>的模板实例来处理的。以下是CClassFatory的实现:
- // CSingleCreator 用于单实例类工厂,这个类为多个CreateObject请求返回相同的对象指针..
- template<class comObj>
- class CSingleCreator
- {
- protected:
- CSingleCreator():m_pObj(0) {};
- comObj *CreateObject()
- {
- if(!m_pObj)
- {
- m_pObj = new comObj;
- }
- return m_pObj;
- }
- comObj * m_pObj;
- };
- // CMultiCreator 用于常用类工厂,这个类为每一个CreateObject请求返回新的对象指针..
- template<class comObj>
- class CMultiCreator
- {
- protected:
- CMultiCreator():m_pObj(0) {};
- comObj *CreateObject()
- {
- return new comObj;
- }
- comObj * m_pObj;
- };
- //ClassFactory类实现
- // MultiCreator是缺省的类工厂创建者
- //这个类实现了接口IclasFactory......
- class CClassFactory : public CComBase<>,
- public InterfaceImpl<IClassFactory>,
- public creatorClass
- {
- public:
- CClassFactory() {};
- virtual ~CClassFactory() {};
- STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv)
- {
- *ppv = NULL;
- if(IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,IID_IClassFactory))
- {
- *ppv = (IClassFactory *) this;
- _AddRef();
- return S_OK;
- }
- return E_NOINTERFACE;
- }
- STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj)
- {
- *ppvObj = NULL;
- if (pUnkOuter)
- return CLASS_E_NOAGGREGATION;
- m_pObj = CreateObject(); // m_pObj 在creatorClass中定义
- if (!m_pObj)
- return E_OUTOFMEMORY;
- HRESULT hr = m_pObj->QueryInterface(riid, ppvObj);
- if(hr != S_OK)
- {
- delete m_pObj;
- }
- return hr;
- }
- STDMETHODIMP LockServer(BOOL) { return S_OK; } // 未实现
- };
COM调用CreateInstance创建请求的对象,参数riid指的是所请求的接口IID,如果这个对象支持这个接口,则增加它的引用计数并返回对自身的引用。
关于代码:本文所提出的方法是如何用纯粹的C++编写COM组件的一个大概念。很多方面的细节都省略了。从本文的文字和代码中可以看出用纯C++编写COM组件需要做些什么工作,如果你要用这种方法编写COM组件的话,这些代码只能是抛砖引玉,具体的实现可以在此基础上往下做.
==================================================文三===========================================================================
最近在学习COM,自己实现了一个小Demo,拿出来和大家分享一下。求各种批评。
我实现的这个组件向外提供了一个接口ICompTest,里面只有一个函数helloworld(),功能为返回一个整数89。
实现了自注册功能。下面贴出代码,希望对刚开始学习COM的朋友有所帮助。
首先看一下工程结构,编译环境为vs 2008
CompTest工程是服务端工程,是一个dll,CtrlTest是客户端工程,是一个控制台工程。
下面通过客户端的运行逻辑来讲述整个运行流程,先看一下CtrlTest.cpp文件
- /**
- * @file CtrlTest.cpp
- * @author LiWang112358
- * @date 2012/3/17
- * @version 1.0
- * @brief COM客户端代码
- */
- #include <iostream>
- #include "ICompTest.h"
- using namespace std;
- int main()
- {
- CoInitialize(NULL); //初始化COM库,使用默认的内存分配器
- IUnknown* pUnknown = NULL;
- GUID CLSID_CompTestClass;
- HRESULT hResult = CLSIDFromProgID(L"COMCTL.CompTest", &CLSID_CompTestClass); //获取ProgID为COMCTL.CompTest组建的CLSID
- if (S_OK != hResult){
- printf("Can't find CLSID!\n");
- return -1;
- }
- else{
- LPOLESTR szCLSID;
- StringFromCLSID(CLSID_CompTestClass, &szCLSID); //将其转化为字符串形式用来输出
- wprintf(L"find CLSID \"%s\"\n",szCLSID);
- CoTaskMemFree(szCLSID); //调用COM库的内存释放
- }
- //用此CLSID创建一个COM对象并获取IUnknown接口
- hResult = CoCreateInstance(CLSID_CompTestClass, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown,(void **)&pUnknown);
- if (S_OK != hResult || NULL == pUnknown){
- printf("Create Object Failed!\n");
- return -1;
- }
- ICompTest* pCompTest = NULL;
- hResult = pUnknown->QueryInterface(IID_ICompTest, (void**)&pCompTest);//通过此结构查询我们自己的ICompTest接口
- cout << pCompTest->HelloWorld() << endl;//调用我们自己接口中的函数
- pCompTest->Release(); //释放自己的接口
- pUnknown->Release(); //释放IUnknown接口
- CoUninitialize(); //COM库反初始化
- return 0;
- }
这是客户程序的主逻辑,主要就是通过COM库创建CompTestClass对象,这个对象在这里是不可见的,这里只能拿到ICompTest接口,通过该接口调用函数HelloWorld。
下面看一下接口的声明ICompTest.h文件,这个文件是客户端和服务端都要有的。
- /**
- * @file ICompTest.h
- * @author LiWang112358
- * @date 2012/3/17
- * @version 1.0
- * @brief ICompTest接口的声明这个文件是服务端和客户端共有的
- */
- #ifndef ICOMPTEST_H
- #define ICOMPTEST_H
- #include <unknwn.h>
- // {81A80687-6CC4-4996-8DD2-F058907FDCA8}
- static const GUID IID_ICompTest =
- { 0x81a80687, 0x6cc4, 0x4996, { 0x8d, 0xd2, 0xf0, 0x58, 0x90, 0x7f, 0xdc, 0xa8 } };
- class ICompTest :
- public IUnknown
- {
- public:
- virtual int _stdcall HelloWorld() = 0;
- };
- #endif
这个文件中有一个GUID IID_ICompTest ,用于查询接口ICompTest 。
我们可以在 CtrlTest.cpp文件中看到CLSIDFromProgID和CoCreateInstance这两个函数,
第一个函数是要通过一个名字"COMCTL.CompTest"拿到一个CLSID,这个过程需要CLSID信息。
第二个函数是要通过这个CLSID找到我们的组件(dll),并加载这个dll,然后创建COM对象,这个过程需要dll的路径信息。
这些信息都被放在注册表中,是这个组件自注册的时候由DllRegisterServer函数写入的。
我们可以先看一下注册之后注册表中的内容
其中COMCTL.CompTest是在键HKEY_CLASSES_ROOT下,{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}是在键HKEY_CLASSES_ROOT\CLSID下。
下面我们看一下这个dll的注册过程。
用“regsvr32.exe dll路径”对dll进行注册,实际上regsvr32只是调用了dll中的DllRegisterServer引出函数。
下面我们看一下DllRegisterServer函数的实现,这个函数在CompTest.cpp中。
- int myReg(LPCWSTR lpPath) //将本组件的信息写入注册表,包括CLSID、所在路径lpPath、ProgID
- {
- HKEY thk, tclsidk;
- //打开键HKEY_CLASSES_ROOT\CLSID,创建新键为CompTestClass的CLSID,
- //在该键下创建键InprocServer32,并将本组件(dll)所在路径lpPath写为该键的默认值
- if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)){
- if (ERROR_SUCCESS == RegCreateKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}", &tclsidk)){
- HKEY tinps32k, tprogidk;
- if (ERROR_SUCCESS == RegCreateKey(tclsidk, L"InprocServer32", &tinps32k)){
- if (ERROR_SUCCESS == RegSetValue(tinps32k, NULL, REG_SZ, lpPath, wcslen(lpPath) * 2)){
- }
- RegCloseKey(tinps32k);
- }
- RegCloseKey(tclsidk);
- }
- RegCloseKey(thk);
- }
- //在键HKEY_CLASSES_ROOT下创建新键为COMCTL.CompTest,
- //在该键下创建子键,并将CompTestClass的CLSID写为该键的默认值
- if (ERROR_SUCCESS == RegCreateKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)){
- if (ERROR_SUCCESS == RegCreateKey(thk, L"CLSID", &tclsidk)){
- if (ERROR_SUCCESS == RegSetValue(tclsidk,
- NULL,
- REG_SZ,
- L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}",
- wcslen(L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}") * 2)){
- }
- }
- }
- //这样的话一个客户端程序如果想要使用本组件,首先可以以COMCTL.CompTest为参数调用CLSIDFromProgID函数
- //来获取CompTestClass的CLSID,再以这个CLSID为参数调用CoCreateInstance创建COM对象
- return 0;
- }
- extern "C" HRESULT _stdcall DllRegisterServer()
- {
- WCHAR szModule[1024];
- DWORD dwResult = GetModuleFileName(g_hModule, szModule, 1024); //获取本组件(dll)所在路径
- if (0 == dwResult){
- return -1;
- }
- MessageBox(NULL, szModule, L"", MB_OK);
- myReg(szModule);//将路径等信息写入注册表
- return 0;
- }
用“regsvr32.exe dll路径 -u”对dll进行反注册,同样,实际上regsvr32只是调用了dll中的DllUnregisterServer引出函数。
下面我们来看一下DllUnregisterServer函数的实现,这个函数在CompTest.cpp中。
- int myDelKey(HKEY hk, LPCWSTR lp)
- {
- if (ERROR_SUCCESS == RegDeleteKey(hk, lp)){
- }
- return 0;
- }
- int myDel() //删除注册时写入注册表的信息
- {
- HKEY thk;
- if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)){
- myDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}\\InprocServer32");
- myDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}");
- RegCloseKey(thk);
- }
- if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)){
- myDelKey(thk, L"CLSID");
- }
- myDelKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest");
- return 0;
- }
- extern "C" HRESULT _stdcall DllUnregisterServer()
- {
- myDel();//删除注册时写入注册表的信息
- return 0;
- }
我们继续分析客户端的代码CoCreateInstance(CLSID_CompTestClass, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown,(void **)&pUnknown);
这个函数是要调用CoGetClassObject函数,来获取CompTestClass的类厂,以此创建CompTestClass对象并获取IUnknown接口。其中,CoGetClassObject函数
实际上是调用了CompTest.cpp中的又一个引出函数DllGetClassObject来获取IClassFactory接口的。最终CoCreateInstance会调用IClassFactory接口的CreateInstance
函数去创建COM对象。
下面我们看一下DllGetClassObject函数的实现
- extern "C" HRESULT _stdcall DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, LPVOID FAR* ppv)//用于创建类厂并返回所需接口,由CoGetClassObject函数调用
- {
- if (CLSID_CompTestClass == rclsid){
- CompTestFactory* pFactory = new CompTestFactory();//创建类厂对象
- if (NULL == pFactory){
- return E_OUTOFMEMORY;
- }
- HRESULT result = pFactory->QueryInterface(riid, ppv);//获取所需接口
- return result;
- }
- else{
- return CLASS_E_CLASSNOTAVAILABLE;
- }
- }
接下来我们看一下组件中的最后一个引出函数DllCanUnloadNow,这样dll中的所有引出函数就都出现了
- extern "C" HRESULT _stdcall DllCanUnloadNow()//用于判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用
- {
- if (0 == g_num){//如果对象个数为0,则可以卸载
- return S_OK;
- }
- else{
- return S_FALSE;
- }
- }
下面我们看一下CompTest.cpp的全貌
- /**
- * @file CompTest.cpp
- * @author LiWang112358
- * @date 2012/3/17
- * @version 1.0
- * @brief 四个重要的引出函数的实现,
- * @ 分别是DllRegisterServer,用于注册本dll,由regsvr32.exe调用
- * @ DllUnregisterServer, 用于反注册本dll,由regsvr32.exe -u 调用
- * @ DllCanUnloadNow,用于判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用
- * @ DllGetClassObject,用于创建类厂并返回所需接口,由CoGetClassObject函数调用
- */
- #include <iostream>
- #include <windows.h>
- #include "factory.h"
- #include "CompTestClass.h"
- using namespace std;
- HMODULE g_hModule; //dll进程实例句柄
- ULONG g_num; //组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载
- int myReg(LPCWSTR lpPath) //将本组件的信息写入注册表,包括CLSID、所在路径lpPath、ProgID
- {
- HKEY thk, tclsidk;
- //打开键HKEY_CLASSES_ROOT\CLSID,创建新键为CompTestClass的CLSID,
- //在该键下创建键InprocServer32,并将本组件(dll)所在路径lpPath写为该键的默认值
- if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)){
- if (ERROR_SUCCESS == RegCreateKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}", &tclsidk)){
- HKEY tinps32k, tprogidk;
- if (ERROR_SUCCESS == RegCreateKey(tclsidk, L"InprocServer32", &tinps32k)){
- if (ERROR_SUCCESS == RegSetValue(tinps32k, NULL, REG_SZ, lpPath, wcslen(lpPath) * 2)){
- }
- RegCloseKey(tinps32k);
- }
- RegCloseKey(tclsidk);
- }
- RegCloseKey(thk);
- }
- //在键HKEY_CLASSES_ROOT下创建新键为COMCTL.CompTest,
- //在该键下创建子键,并将CompTestClass的CLSID写为该键的默认值
- if (ERROR_SUCCESS == RegCreateKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)){
- if (ERROR_SUCCESS == RegCreateKey(thk, L"CLSID", &tclsidk)){
- if (ERROR_SUCCESS == RegSetValue(tclsidk,
- NULL,
- REG_SZ,
- L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}",
- wcslen(L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}") * 2)){
- }
- }
- }
- //这样的话一个客户端程序如果想要使用本组件,首先可以以COMCTL.CompTest为参数调用CLSIDFromProgID函数
- //来获取CompTestClass的CLSID,再以这个CLSID为参数调用CoCreateInstance创建COM对象
- return 0;
- }
- extern "C" HRESULT _stdcall DllRegisterServer()
- {
- WCHAR szModule[1024];
- DWORD dwResult = GetModuleFileName(g_hModule, szModule, 1024); //获取本组件(dll)所在路径
- if (0 == dwResult){
- return -1;
- }
- MessageBox(NULL, szModule, L"", MB_OK);
- myReg(szModule);//将路径等信息写入注册表
- return 0;
- }
- int myDelKey(HKEY hk, LPCWSTR lp)
- {
- if (ERROR_SUCCESS == RegDeleteKey(hk, lp)){
- }
- return 0;
- }
- int myDel() //删除注册时写入注册表的信息
- {
- HKEY thk;
- if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)){
- myDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}\\InprocServer32");
- myDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}");
- RegCloseKey(thk);
- }
- if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)){
- myDelKey(thk, L"CLSID");
- }
- myDelKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest");
- return 0;
- }
- extern "C" HRESULT _stdcall DllUnregisterServer()
- {
- myDel();//删除注册时写入注册表的信息
- return 0;
- }
- extern "C" HRESULT _stdcall DllCanUnloadNow()//用于判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用
- {
- if (0 == g_num){//如果对象个数为0,则可以卸载
- return S_OK;
- }
- else{
- return S_FALSE;
- }
- }
- extern "C" HRESULT _stdcall DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, LPVOID FAR* ppv)//用于创建类厂并返回所需接口,由CoGetClassObject函数调用
- {
- if (CLSID_CompTestClass == rclsid){
- CompTestFactory* pFactory = new CompTestFactory();//创建类厂对象
- if (NULL == pFactory){
- return E_OUTOFMEMORY;
- }
- HRESULT result = pFactory->QueryInterface(riid, ppv);//获取所需接口
- return result;
- }
- else{
- return CLASS_E_CLASSNOTAVAILABLE;
- }
- }
- BOOL APIENTRY DllMain( HMODULE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
- {
- g_hModule = hModule;//获取进程实例句柄,用于获取本组件(dll)路径
- switch (ul_reason_for_call)
- {
- case DLL_PROCESS_ATTACH:
- case DLL_THREAD_ATTACH:
- case DLL_THREAD_DETACH:
- case DLL_PROCESS_DETACH:
- break;
- }
- return TRUE;
- }
下面是.def文件mydef.def用于声明dll的引出函数
- LIBRARY "CompTest"
- EXPORTS
- DllCanUnloadNow
- DllGetClassObject
- DllUnregisterServer
- DllRegisterServer
下面是剩下的文件,有些许注释,语焉不详之处,望海涵。
CompTestClass.h
- /**
- * @file CompTestClass.h
- * @author LiWang112358
- * @date 2012/3/17
- * @version 1.0
- * @brief CompTestClass对象的声明,这个类要实现ICompTest接口
- */
- #ifndef COMPTESTCLASS_H
- #define COMPTESTCLASS_H
- #include "ICompTest.h"
- // {9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}
- static const GUID CLSID_CompTestClass =
- { 0x9ca9dbe8, 0xc0b1, 0x42c9, { 0xb6, 0xc7, 0x85, 0x6b, 0xe5, 0x75, 0x68, 0x55 } };
- class CompTestClass :
- public ICompTest
- {
- public:
- CompTestClass();
- ~CompTestClass();
- //要实现IUnknown接口
- virtual HRESULT _stdcall QueryInterface(const IID& riid, void** ppvObject);
- virtual ULONG _stdcall AddRef();
- virtual ULONG _stdcall Release();
- //要实现ICompTest接口
- virtual int _stdcall HelloWorld();
- protected:
- ULONG m_Ref;
- };
- #endif
CompTestClass.cpp
- /**
- * @file CompTestClass.cpp
- * @author LiWang112358
- * @date 2012/3/17
- * @version 1.0
- * @brief CompTestClass类的实现
- */
- #include "CompTestClass.h"
- ULONG CompTestClass::m_objNum = 0;//组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载
- CRITICAL_SECTION CompTestClass::m_cs;//为了多线程调用对m_objNum加的锁
- CompTestClass::CompTestClass()
- {
- m_Ref = 0;
- autoLock tlock(m_cs);
- m_objNum ++; //构造了一个对象
- }
- CompTestClass::~CompTestClass()
- {
- autoLock tlock(m_cs);
- m_objNum --; //释放了一个对象
- }
- HRESULT _stdcall CompTestClass::QueryInterface(const IID &riid, void **ppvObject)
- {
- if (IID_IUnknown == riid){
- *ppvObject = (IUnknown*)this;
- ((IUnknown*)(*ppvObject))->AddRef();
- }
- else if (IID_ICompTest == riid){
- *ppvObject = (ICompTest*)this;
- ((ICompTest*)(*ppvObject))->AddRef();
- }
- else{
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- return S_OK;
- }
- ULONG _stdcall CompTestClass::AddRef()
- {
- m_Ref ++;
- return m_Ref;
- }
- ULONG _stdcall CompTestClass::Release()
- {
- m_Ref --;
- if (0 == m_Ref){
- delete this;
- return 0;
- }
- return m_Ref;
- }
- int _stdcall CompTestClass::HelloWorld()//ICompTest接口的实现,返回一个整数89
- {
- return 89;
- }
- int CompTestClass::Init()
- {
- m_objNum = 0;
- InitializeCriticalSection(&m_cs);
- return 0;
- }
- ULONG CompTestClass::ObjNum()
- {
- return m_objNum;
- }
factory.h
- /**
- * @file factory.h
- * @author LiWang112358
- * @date 2012/3/17
- * @version 1.0
- * @brief CompTestClass对象的类厂的声明
- */
- #ifndef FACTORY_H
- #define FACTORY_H
- #include <unknwn.h>
- class CompTestFactory :
- public IClassFactory
- {
- public:
- CompTestFactory();
- ~CompTestFactory();
- //要实现IUnknown接口
- virtual HRESULT _stdcall QueryInterface(const IID& riid, void** ppvObject);
- virtual ULONG _stdcall AddRef();
- virtual ULONG _stdcall Release();
- //要实现IClassFactory接口
- virtual HRESULT _stdcall CreateInstance(IUnknown *pUnkOuter, const IID& riid, void **ppvObject);
- virtual HRESULT _stdcall LockServer(BOOL fLock);
- protected:
- ULONG m_Ref;
- };
- #endif
factory.cpp
- /**
- * @file factory.cpp
- * @author LiWang112358
- * @date 2012/3/17
- * @version 1.0
- * @brief CompTestClass对象的类厂的实现
- */
- #include "factory.h"
- #include "CompTestClass.h"
- CompTestFactory::CompTestFactory()
- {
- m_Ref = 0;
- }
- CompTestFactory::~CompTestFactory()
- {
- }
- HRESULT _stdcall CompTestFactory::QueryInterface(const IID &riid, void **ppvObject)
- {
- if (IID_IUnknown == riid){
- *ppvObject = (IUnknown*)this;
- ((IUnknown*)(*ppvObject))->AddRef();
- }
- else if (IID_IClassFactory == riid){
- *ppvObject = (IClassFactory*)this;
- ((IClassFactory*)(*ppvObject))->AddRef();
- }
- else{
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- return S_OK;
- }
- ULONG _stdcall CompTestFactory::AddRef()
- {
- m_Ref ++;
- return m_Ref;
- }
- ULONG _stdcall CompTestFactory::Release()
- {
- m_Ref --;
- if (0 == m_Ref){
- delete this;
- return 0;
- }
- return m_Ref;
- }
- HRESULT _stdcall CompTestFactory::CreateInstance(IUnknown *pUnkOuter, const IID &riid, void **ppvObject)//最重要的函数,这个函数创建CompTestClass对象,并返回所需接口
- {
- if (NULL != pUnkOuter){
- return CLASS_E_NOAGGREGATION;
- }
- HRESULT hr = E_OUTOFMEMORY;
- CompTestClass::Init();
- CompTestClass* pObj = new CompTestClass();
- if (NULL == pObj){
- return hr;
- }
- hr = pObj->QueryInterface(riid, ppvObject);
- if (S_OK != hr){
- delete pObj;
- }
- return hr;
- }
- HRESULT _stdcall CompTestFactory::LockServer(BOOL fLock)
- {
- return NOERROR;
- }
以下是客户程序的运行结果
- C#.NET com组件的编写
- C++ COM组件的编写
- 编写C++C#相互调用COM组件
- 用C shape编写COM组件
- PHP4调用自己编写的COM组件
- 用纯粹的C++编写COM组件
- PHP4调用自己编写的COM组件
- 用纯粹的C++编写COM组件
- ASP使用C#编写的COM+组件
- PHP4调用自己编写的COM组件
- 用纯粹的C++编写COM组件
- 用纯粹的C++编写COM组件
- 用纯粹的C++编写COM组件
- 用纯粹的C++编写COM组件
- 用纯粹的C++编写COM组件
- 用纯粹的C++编写COM组件
- 用纯粹的C++编写COM组件
- 用纯粹的C++编写COM组件
- HDU 2124
- keypad 驱动分析
- m3图像显示不完整,左侧被拉伸
- C++并发实战:面试题6:线程一次性同步
- spring源码解析的好文章
- C++ COM组件的编写
- Android开源测试框架学习
- 链表算法小记录
- iOS 数据持久化二-对象归档
- Deformable Part Model的学习
- android硬件加速
- tomcat端口号修改
- 权限不挃令间癿关系:
- Fedora 安装flash