这就是COM组件


Author: ume Date:2012-02-21


   自从微软推出.NET以来,COM技术就渐渐淡出人们的视野,然而这并不意味COM不再发挥作用,相反,COM非常重要。可以说.NET的实现离不开COM的支撑。COM是好东西,但是它太难了,不利于产品推广,于是微软只能在之上增加一层封装从而提高产品的易用性。对COM有所了解是很有必要的,希望这篇文章给你带来一点启发。

1. COM的思想

    开篇就讲COM的思想肯定让人泄气,因为它极有可能抽象空洞、晦涩难懂。换个角度来说,我觉得COM的思想仍然是需要自己去体会的,这里给出的不过是一个思考的线索而已,你大可不求甚解。

    软件的开发是永无止境的,只要软件还在存活期,它就应当不断被完善和更新。一款软件可能十分庞大,但真正可变的部分却是很有限的。我们当然希望在更新软件的时候,只更新变化的部分,而不要笨手笨脚把整个软件都换掉。只更新变化的部分?这就要求模块支持动态链接。所谓动态链接就是模块只在被使用的时候才被加载,程序调用模块中的某个函数是通过指针来完成的。动态链接允许模块分离出去,而不像静态链接那样须经过编译才能整合到程序中来。dll是实现动态链接的一种方法,它使更新软件的工作浓缩成了更新dll,用户无需重新安装软件,只需替换相应的dll文件就能实现软件的升级。

    动态链接是针对普通用户而言的,现在换一个对象:模块的用户。模块的用户是应用程序开发人员,对于模块的提供商来说也算得上同行了,只不过术业有专攻,各自工作的重点不同而已。显然采用dll的形式,模块的提供商可以很方便的发布自己的产品。其中不可忽视的另一点即信息的封装(或称隐藏),即将模块的实现细节隐蔽起来,用户无法知道模块的提供商采用何种语言、何种算法,简而言之就是用户看不到模块的源代码。dll是二进制级别上的代码复用,它实现了信息的封装。

    综上所述,软件开发要求模块支持“动态链接”和“信息封装”,更直白地说就是要求模块和客户代码之间更低的耦合度。把模块制作成组件是必然的选择,而COM本质上是一种创建组件的方法和规范。

    注:dll并不等同于组件,它只是组件的一种形式。由于dll的易用性,它的应用很广泛。

2. 实例说明

    我们创建一个COM组件,它将实现接口ICouplet,用户可通过该接口调用what()方法输出一副对联。what()方法不值一提,不过你可以将它当作程序可变的部分。我们创建的COM组件也要实现接口IClassFactory,它是创建组件的简单组件。之所以这么设计是为了让组件与客户代码彻底脱耦,尽可能少的联系。

    除了实现接口ICouplet和IClassFactory外, COM组件还要能实现自注册,因此它必须导出函数DllRegister/DllUnregister。另外两个导出函数DllCanUnloadNow和DllGetClassObject也非常重要,前者用来询问当前dll能否被卸载,它被CoFreeUnusedLibraries调用;后者用来创建类厂组件,它被CoCreateInstance调用。名称形如Coxxx的函数是COM库函数,它是实现COM组件的公共操作,由微软提供,类似于Win32 API。我们常见的客户代码中CoInitialize/CoUninitialize函数就起到初始化和卸载COM库的作用。要导出上述4个函数就必须编写一个.def文件,具体写法见代码清单。

    最后要说明的是COM组件的自注册。我们知道注册表是Windows的公共系统数据库,其中记录了软件、硬件、用户配置等信息。而COM组件是用一个128比特的GUID标识的,为了使得COM组件的安装目录更灵活,我们可以在注册表中对它进行注册,注册的主要信息即COM组件的GUID标识与其存储路径的对应关系,在使用该组件时就到注册表中去查找。注册一个COM组件一般使用regsvr32.exe程序来完成,当然你也可以自己写一个类似于regsvr32.exe的小程序来完成COM组件的注册,regsvr32.exe本质上调用了组件的导出函数DllRegister/DllUnregister

    生成Couplet.dll文件后,首先在控制台注册它。具体方法:切换到Couplet.dll所在目录,输入指令regsvr32 Couplet.dll。然后运行客户程序Reader.exe,其结果如下所示:

  1. Create Couplet object  
  2. Succeeded in getting the pointer to ICouplet  
  3.   
  4. 1st Scroll: Study Hard, Work Hard, Make Money More and More  
  5. 2nd Scroll: Eat Well, Sleep Well, Have Fun Day by Day  
  6. Top Scroll: Gelievable  
  7.   
  8. Couplet object deleted  
  9. 请按任意键继续. . .  

    然后修改Couplet::what()方法,让它输出中文,重新生成Couplet.dll。这一步不用重新注册Couplet.dll,因为Couplet.dll的路径没变,CLSID_Couplet也没变。运行客户程序Reader.exe,其结果如下所示:

  1. CreateCouplet object  
  2.   
  3. Succeededin getting the pointer to ICouplet  
  4.   
  5.    
  6.   
  7. 上联:我爱的人名花有主  
  8.   
  9. 下联:爱我的人惨不忍睹  
  10.   
  11. 横批:命苦  
  12.   
  13.    
  14.   
  15. Coupletobject deleted  
  16.   
  17. 请按任意键继续. . .  

    这个例子证明了COM组件的更新不会对客户端造成影响,使用COM组件可以实现模块与客户代码彻底脱耦。实验结束后,在控制台输入指令regsvr32 /u Couplet.dll,从注册表中将dll模块信息清除。

3. 代码清单

  1. /* File List: (COM) IFace.h Register.h Register.cpp Couplet.cpp Couple.def 
  2.  *           (Client) IFace.h Reader.cpp 
  3.  * date: 2012-02-21 
  4.  * author: ume 
  5.  */  
  6. /////////////////////////////////////////////////////////////////  
  7. // IFace.h 接口的声明,组件ID、接口ID的定义  
  8. //  
  9. #include <ObjBase.h>  
  10. // interface  
  11. interface ICouplet : IUnknown  
  12. {  
  13.     virtual void what() = 0;  
  14. };  
  15. // GUIDs  
  16. // {03844548-B0B9-4B12-869D-061AAE2E4B7F}  
  17. static const GUID IID_ICouplet =   
  18. { 0x3844548, 0xb0b9, 0x4b12, { 0x86, 0x9d, 0x6, 0x1a, 0xae, 0x2e, 0x4b, 0x7f } };  
  19. // {26615B48-1D2E-4A40-9C07-AD5B1B48368C}  
  20. static const GUID CLSID_Couplet =   
  21. { 0x26615b48, 0x1d2e, 0x4a40, { 0x9c, 0x7, 0xad, 0x5b, 0x1b, 0x48, 0x36, 0x8c } };  
  22. /////////////////////////////////////////////////////////////////  
  23. // Register.h 注册函数的声明  
  24. //  
  25. HRESULT RegisterServer(HMODULE hModule,                 
  26.     const CLSID& clsid,                 
  27.     const char* szFriendlyName,             
  28.     const char* szVerIndProgID,             
  29.     const char* szProgID);  
  30. HRESULT UnRegisterServer(const CLSID& clsid,          
  31.     const char* szVerIndProgID,           
  32.     const char* szProgID);  
  33. /////////////////////////////////////////////////////////////////  
  34. // Register.cpp 注册函数的定义  
  35. // 这些函数可重复使用,非本文重点  
  36. //  
  37. #include <objbase.h>  
  38. #include "Register.h"  
  39. //set the given key and its value;  
  40. BOOL setKeyAndValue(const char* pszPath,  
  41.                     const char* szSubkey,  
  42.                     const char* szValue);  
  43. //Convert a CLSID into a char string  
  44. void CLSIDtochar(const CLSID& clsid,  
  45.                  char* szCLSID,  
  46.                  int length);  
  47. //Delete szKeyChild and all of its descendents  
  48. LONG recursiveDeleteKey(HKEY hKeyParent,const char* szKeyChild);  
  49. //size of a CLSID as a string  
  50. const int CLSID_STRING_SIZE = 39;  
  51. //Register the component in the registry  
  52. HRESULT RegisterServer(HMODULE hModule,  
  53.                        const CLSID& clsid,  
  54.                        const char* szFriendlyName,  
  55.                        const char* szVerIndProgID,  
  56.                        const char* szProgID)  
  57. {  
  58.     //Get the Server location  
  59.     char szModule[512];  
  60.     DWORD dwResult = ::GetModuleFileName(hModule,szModule,sizeof(szModule)/sizeof(char));  
  61.     assert(dwResult!=0);  
  62.   
  63.     //Convert the CLSID into a char  
  64.     char szCLSID[CLSID_STRING_SIZE];  
  65.     CLSIDtochar(clsid,szCLSID,sizeof(szCLSID));  
  66.   
  67.     //Build the key CLSID\\{}  
  68.     char szKey[64];  
  69.     strcpy(szKey,"CLSID\\");  
  70.     strcat(szKey,szCLSID);  
  71.   
  72.     //Add the CLSID to the registry  
  73.     setKeyAndValue(szKey,NULL,szFriendlyName);  
  74.   
  75.     //Add the Server filename subkey under the CLSID key  
  76.     setKeyAndValue(szKey,"InprocServer32",szModule);  
  77.   
  78.     setKeyAndValue(szKey,"ProgID",szProgID);  
  79.   
  80.     setKeyAndValue(szKey,"VersionIndependentProgID",szVerIndProgID);  
  81.   
  82.     //Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT  
  83.     setKeyAndValue(szVerIndProgID,NULL,szFriendlyName);  
  84.     setKeyAndValue(szVerIndProgID,"CLSID",szCLSID);  
  85.     setKeyAndValue(szVerIndProgID,"CurVer",szProgID);  
  86.   
  87.     //Add the versioned ProgID subkey under HKEY_CLASSES_ROOT  
  88.     setKeyAndValue(szProgID,NULL,szFriendlyName);  
  89.     setKeyAndValue(szProgID,"CLSID",szCLSID);  
  90.     return S_OK;  
  91. }  
  92.   
  93. //  
  94. //Remove the component from the register  
  95. //  
  96. HRESULT UnRegisterServer(const CLSID& clsid,           // Class ID  
  97.                          const char* szVerIndProgID,   // Programmatic  
  98.                          const char* szProgID)           // IDs  
  99. {  
  100.     //Convert the CLSID into a char.  
  101.     char szCLSID[CLSID_STRING_SIZE];  
  102.     CLSIDtochar(clsid,szCLSID,sizeof(szCLSID));  
  103.   
  104.     //Build the key CLSID\\{}  
  105.     char szKey[64];  
  106.     strcpy(szKey,"CLSID\\");  
  107.     strcat(szKey,szCLSID);  
  108.   
  109.     //Delete the CLSID key - CLSID\{}  
  110.     LONG lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szKey);  
  111.     assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));  
  112.   
  113.     //Delete the version-independent ProgID Key  
  114.     lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szVerIndProgID);  
  115.     assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));  
  116.   
  117.     //Delete the ProgID key.  
  118.     lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szProgID);  
  119.     assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));  
  120.   
  121.     return S_OK;  
  122. }  
  123. //Convert a CLSID to a char string  
  124. void CLSIDtochar(const CLSID& clsid,  
  125.                  char* szCLSID,  
  126.                  int length)  
  127. {  
  128.     assert(length>=CLSID_STRING_SIZE);  
  129.   
  130.     //Get CLSID  
  131.     LPOLESTR wszCLSID = NULL;  
  132.     HRESULT hr = StringFromCLSID(clsid,&wszCLSID);  
  133.     assert(SUCCEEDED(hr));  
  134.   
  135.     //Convert from wide characters to non_wide  
  136.     wcstombs(szCLSID,wszCLSID,length);  
  137.       
  138.     //Free memory  
  139.     CoTaskMemFree(wszCLSID);  
  140. }  
  141. // Delete a Key and all of its descendents  
  142. LONG recursiveDeleteKey(HKEY hKeyParent,const char* lpszKeyChild)  
  143. {  
  144.     //Open the child.  
  145.     HKEY hKeyChild;  
  146.     LONG lRes = RegOpenKeyEx(hKeyParent,lpszKeyChild,0,KEY_ALL_ACCESS,&hKeyChild);  
  147.   
  148.     if(lRes != ERROR_SUCCESS)  
  149.         return lRes;  
  150.   
  151.     //Enumerate all of the decendents of this child  
  152.     FILETIME time;  
  153.     char szBuffer[256];  
  154.     DWORD dwSize = 256 ;  
  155.       
  156.     while(RegEnumKeyEx(hKeyChild,0,szBuffer,&dwSize,NULL,  
  157.         NULL,NULL,&time) == S_OK)  
  158.     {  
  159.         //Delete the decendents of this child.  
  160.         lRes = recursiveDeleteKey(hKeyChild,szBuffer);  
  161.         if(lRes != ERROR_SUCCESS)  
  162.         {  
  163.             RegCloseKey(hKeyChild);  
  164.             return lRes;  
  165.         }  
  166.         dwSize = 256;  
  167.     }  
  168.     RegCloseKey(hKeyChild);  
  169.     return RegDeleteKey(hKeyParent,lpszKeyChild);  
  170. }  
  171.   
  172. BOOL setKeyAndValue(const char* szKey,  
  173.                     const char* szSubkey,  
  174.                     const char* szValue)  
  175. {  
  176.     HKEY hKey;  
  177.     char szKeyBuf[1024];  
  178.   
  179.     //Copy keyname into buffer.  
  180.     strcpy(szKeyBuf,szKey);  
  181.   
  182.     //Add subkey name to buffer.  
  183.     if(szSubkey!=NULL)  
  184.     {  
  185.         strcat(szKeyBuf,"\\");  
  186.         strcat(szKeyBuf,szSubkey);  
  187.     }  
  188.   
  189.     // Create and open key and subkey.  
  190.     long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT ,  
  191.                                   szKeyBuf,   
  192.                                   0, NULL, REG_OPTION_NON_VOLATILE,  
  193.                                   KEY_ALL_ACCESS, NULL,   
  194.                                   &hKey, NULL) ;  
  195.     if (lResult != ERROR_SUCCESS)  
  196.   
  197.     {  
  198.         return FALSE ;  
  199.     }  
  200.   
  201.     // Set the Value.  
  202.     if (szValue != NULL)  
  203.     {  
  204.         RegSetValueEx(hKey, NULL, 0, REG_SZ,   
  205.                       (BYTE *)szValue,   
  206.                       strlen(szValue)+1) ;  
  207.     }  
  208.   
  209.     RegCloseKey(hKey) ;  
  210.     return TRUE ;  
  211. }  
  212. /////////////////////////////////////////////////////////////////  
  213. // Couplet.cpp 接口的实现  
  214. // 本文的重点,尤其是Couplet和CFactory的实现  
  215. //  
  216. #include "IFace.h"  
  217. #include "Register.h"  
  218. #include <iostream>  
  219. using namespace std;  
  220. // trace  
  221. void trace(const char* msg) { cout<<msg<<endl; }  
  222. // global variables  
  223. HMODULE g_hModule;  
  224. static long g_cComponents = 0;  
  225. static long g_cLocks = 0;  
  226. // Friendly name of component  
  227. const char g_szFriendlyName[] = "A Couplet";  
  228. // Version independent ProgID  
  229. const char g_szVerIndProgID[] = "Couplet.Test";  
  230. // ProgID  
  231. const char g_szProgID[] = "Couplet.Test.1";  
  232. // implementation  
  233. class Couplet : public ICouplet  
  234. {  
  235. public:  
  236.     virtual LRESULT __stdcall QueryInterface(const IID& iid, void** ppv);  
  237.     virtual ULONG __stdcall AddRef()  
  238.     {  
  239.         return ::InterlockedIncrement(&m_cRef);  
  240.     }  
  241.     virtual ULONG __stdcall Release()  
  242.     {  
  243.         if(::InterlockedDecrement(&m_cRef) == 0)  
  244.         {  
  245.             delete this;  
  246.             return 0;  
  247.         }  
  248.         return m_cRef;  
  249.     }  
  250.     virtual void what()   
  251.     {   
  252.         //cout<<"\n上联:我爱的人名花有主\n下联:爱我的人惨不忍睹\n横批:命苦\n\n";   
  253.         cout<<"\n1st Scroll: Study Hard, Work Hard, Make Money More and More\n\  
  254. 2nd Scroll: Eat Well, Sleep Well, Have Fun Day by Day\nTop Scroll: Gelievable\n\n";   
  255.     }  
  256.     // constructor  
  257.     Couplet() : m_cRef(1)   
  258.     {  
  259.         ::InterlockedIncrement(&g_cComponents);  
  260.         trace("Create Couplet object");   
  261.     }  
  262.     // destructor  
  263.     ~Couplet()  
  264.     {  
  265.         ::InterlockedDecrement(&g_cComponents);  
  266.         trace("Couplet object deleted");  
  267.     }  
  268. private:  
  269.     long m_cRef;  
  270. };  
  271. // definition of QueryInterface  
  272. LRESULT __stdcall Couplet::QueryInterface(const IID& iid, void** ppv)  
  273. {  
  274.     if((iid == IID_IUnknown) || (iid == IID_ICouplet))  
  275.     {  
  276.         *ppv = static_cast<ICouplet*>(this);  
  277.     }  
  278.     else  
  279.     {  
  280.         *ppv = NULL;  
  281.         return E_NOINTERFACE;  
  282.     }  
  283.     static_cast<IUnknown*>(*ppv)->AddRef();  
  284.     return S_OK;  
  285. }  
  286. // class CFactory  
  287. class CFactory : public IClassFactory  
  288. {  
  289. public:  
  290.     virtual LRESULT __stdcall QueryInterface(const IID& iid, void** ppv);  
  291.     virtual ULONG __stdcall AddRef()  
  292.     {  
  293.         return ::InterlockedIncrement(&m_cRef);  
  294.     }  
  295.     virtual ULONG __stdcall Release()  
  296.     {  
  297.         if(::InterlockedDecrement(&m_cRef) == 0)  
  298.         {  
  299.             delete this;  
  300.             return 0;  
  301.         }  
  302.         return m_cRef;  
  303.     }  
  304.     virtual LRESULT __stdcall CreateInstance(IUnknown* pCmpntOuter,  
  305.                                    const IID& iid,  
  306.                                    void** ppv);  
  307.     virtual LRESULT __stdcall LockServer(BOOL bLock);  
  308. private:  
  309.     long m_cRef;  
  310. };  
  311. // definition of QueryInterface  
  312. LRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)  
  313. {  
  314.     if((iid == IID_IUnknown) || (iid == IID_IClassFactory))  
  315.     {  
  316.         *ppv = static_cast<IClassFactory*>(this);  
  317.     }  
  318.     else  
  319.     {  
  320.         *ppv = NULL;  
  321.         return E_NOINTERFACE;  
  322.     }  
  323.     static_cast<IUnknown*>(*ppv)->AddRef();  
  324.     return S_OK;  
  325. }  
  326. // definition of CreateInstance  
  327. LRESULT __stdcall CFactory::CreateInstance(IUnknown* pCmpntOuter,  
  328.                                  const IID& iid,  
  329.                                  void** ppv)  
  330. {  
  331.     if(pCmpntOuter != NULL)  
  332.     {  
  333.         cout<<"No Aggregate in this Class Factory"<<endl;  
  334.         return CLASS_E_NOAGGREGATION;  
  335.     }  
  336.     Couplet* pCouplet = new Couplet;  
  337.     if(pCouplet == NULL)  
  338.         return E_OUTOFMEMORY;  
  339.     HRESULT hr = pCouplet->QueryInterface(iid, ppv);  
  340.     pCouplet->Release();  
  341.     return hr;  
  342. }  
  343. // definition of LockServer  
  344. LRESULT __stdcall CFactory::LockServer(BOOL bLock)  
  345. {  
  346.     if(bLock)  
  347.     {  
  348.         ::InterlockedIncrement(&g_cLocks);  
  349.     }  
  350.     else  
  351.     {  
  352.         ::InterlockedDecrement(&g_cLocks);  
  353.     }  
  354.     return S_OK;  
  355. }  
  356. STDAPI DllCanUnloadNow()  
  357. {  
  358.     if((g_cComponents == 0) && (g_cLocks == 0))  
  359.     {  
  360.         return S_OK;  
  361.     }  
  362.     else  
  363.     {  
  364.         return S_FALSE;  
  365.     }  
  366. }  
  367. // Get class factory  
  368. STDAPI DllGetClassObject(const CLSID& clsid,  
  369.                          const IID& iid,  
  370.                          void** ppv)  
  371. {  
  372.     // Can we create this component?  
  373.     if(clsid != CLSID_Couplet)  
  374.     {  
  375.         return CLASS_E_CLASSNOTAVAILABLE;  
  376.     }  
  377.     // Create class factory  
  378.     CFactory* pFactory = new CFactory;  
  379.     if(pFactory == NULL)  
  380.     {  
  381.         return E_OUTOFMEMORY;  
  382.     }  
  383.     // Get requested interface  
  384.     HRESULT hr = pFactory->QueryInterface(iid, ppv);  
  385.     pFactory->Release();  
  386.     return hr;  
  387. }  
  388. // register and unregister component  
  389. STDAPI DllRegisterServer()  
  390. {  
  391.     return RegisterServer(g_hModule,  
  392.                           CLSID_Couplet,  
  393.                           g_szFriendlyName,  
  394.                           g_szVerIndProgID,  
  395.                           g_szProgID);  
  396. }  
  397. STDAPI DllUnregisterServer()  
  398. {  
  399.     return UnRegisterServer(CLSID_Couplet,  
  400.                             g_szVerIndProgID,  
  401.                             g_szProgID);  
  402. }  
  403. // dll main  
  404. BOOL APIENTRY DllMain(HANDLE hModule,  
  405.                      DWORD dwReason,  
  406.                      void* lpReserved)  
  407. {  
  408.     if(dwReason == DLL_PROCESS_ATTACH)  
  409.     {  
  410.         g_hModule = (HMODULE)hModule;  
  411.     }  
  412.     return TRUE;  
  413. }  
  414. /////////////////////////////////////////////////////////////////  
  415. // Couplet.def 模块定义文件  
  416. //  
  417. LIBRARY Couplet.dll  
  418. EXPORTS  
  419.     DllCanUnloadNow @1 PRIVATE  
  420.     DllGetClassObject @2 PRIVATE  
  421.     DllRegisterServer @3 PRIVATE  
  422.     DllUnregisterServer @4 PRIVATE  
  423. /////////////////////////////////////////////////////////////////  
  424. // Reader.cpp 通过ICouplet接口调用what()方法读取对联内容  
  425. // 注意: 客户端的IFace.h与COM组件中的IFace.h完全一样  
  426. //  
  427. #include <iostream>  
  428. #include <ObjBase.h>  
  429. #include "IFace.h"  
  430. using namespace std;  
  431. // global function  
  432. void trace(const char* pMsg){ cout<<pMsg<<endl; }  
  433. // main function  
  434. int main()  
  435. {  
  436.     ::CoInitialize(NULL);  
  437.     ICouplet* pICouplet = NULL;  
  438.     HRESULT hr = ::CoCreateInstance(CLSID_Couplet, NULL, CLSCTX_INPROC_SERVER, IID_ICouplet,   
  439.                                    (void**)&pICouplet);  
  440.     if(SUCCEEDED(hr))  
  441.     {  
  442.         trace("Succeeded in getting the pointer to ICouplet");  
  443.         pICouplet->what();  
  444.         pICouplet->Release();  
  445.     }  
  446.     else   
  447.     {  
  448.         trace("Failed to get the pointer to ICouplet");  
  449.     }  
  450.     ::CoUninitialize();  
  451.     system("pause");  
  452.     return 0;  
  453. }  

posted on 2012-02-21 16:30  Hibernate4  阅读(244)  评论(0编辑  收藏  举报

导航