这就是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. }  
组件基础 1 软件开发的阶段 1.1 结构化编程 采用自顶向下的编程方式,划分模块 和功能的一种编程方式。 1.2 面向对象编程 采用对象的方式,将程序抽象成类, 模拟现实世界,采用继承、多态的方式 设计软件的一种编程方式。 1.3 面向组件编程 将功能和数据封装成二进制代码,采用 搭积木的方式实现软件的一种编程方式。 2 组件和优点 2.1 组件 - 实际是一些可以执行的二进 制程序,它可以给其他的应用程序、操 作系统或其他组件提供功能 2.2 优点 2.2.1 可以方便的提供软件定制机制 2.2.2 可以很灵活的提供功能 2.2.3 可以很方便的实现程序的分布式 开发。 3 组件的标准 - COMComponent Object Model ) 3.1 COM是一种编程规范,不论任何开发语言 要实现组件都必须按照这种规范来实现。 组件和开发语言无关。 这些编程规范定义了组件的操作、接口的 访问等等。 3.2 COM接口 COM接口是组件的核心,从一定程度上 讲"COM接口是组件的一切". COM接口给用户提供了访问组件的方式. 通过COM接口提供的函数,可以使用组件 的功能. 4 COM组件 4.1 COM组件-就是在Windows平台下, 封装在动态库(DLL)或者可执行文件(EXE) 中的一段代码,这些代码是按照COM的 规范实现. 4.2 COM组件的特点 4.2.1 动态链接 4.2.2 与编程语言无关 4.2.3 以二进制方式发布 二 COM接口 1 接口的理解 DLL的接口 - DLL导出的函数 类的接口 - 类的成员函数 COM接口 - 是一个包含了一组函数指针 的数据结构,这些函数是由组件实现的 2 C++的接口实现 2.1 C++实现接口的方式,使用抽象类 定义接口. 2.2 基于抽象类,派生出子类并实现 功能. 2.3 使用 interface 定义接口 interface ClassA { }; 目前VC中,interface其实就是struct 3 接口的动态导出 3.1 DLL的实现 3.1.1 接口的的定义 3.1.2 接口的实现 3.1.3 创建接口的函数 3.2 DLL的使用 3.2.1 加载DLL和获取创建接口的函数 3.2.2 创建接口 3.2.3 使用接口的函数 4 接口的生命期 4.1 问题 在DLL中使用new创建接口后,在用户 程序使用完该接口后,如果使用delete 直接删除,会出现内存异常. 每个模块有自己的内存堆(crtheap) EXE - crtheap DLL - crtheap new/delete/malloc/free默认情况 下都是从自己所在模块内存堆(crtheap) 中分配和施放内存.而各个模块的 这个内存堆是各自独立.所以在DLL中 使用new分配内存,不能在EXE中delete. 4.2 引用计数和AddRef/Release函数 引用计数 - 就是一个整数,作用是 表示接口的使用次数 AddRef - 增加引用计数 +1 Release - 减少引用计数 -1, 如果 当引用计数为0,接口被删除 4.3 使用 4.3.1 创建接口 4.3.2 调用AddRef,增加引用计数 4.3.3 使用接口 4.3.4 调用Release,减少引用计数 4.4 注意 4.4.1 在调用Release之后,接口指针 不能再使用 4.4.2 多线程情况下,接口引用计数 要使用原子锁的方式进行加减 5 接口的查询 5.1 每个接口都具有唯一标识 GUID 5.2 实现接口查询函数 QueryInterface 6 IUnknown 接口 6.1 IUnknown是微软定义的标准接口 我们实现所有接口就是继承这个接口 6.2 IUnknown定义了三个函数 QueryInterface 接口查询函数 AddRef 增加引用计数 Release 减少引用计数 7 接口定义语言 - IDL(Interface Definition Language ) 7.1 IDL和MIDL IDL - 定义接口的一种语言,与开发 语言无关. MIDL.EXE - 可以将IDL语言定义接口, 编译成C++语言的接口定义 7.2 IDL的基础 import "XXXX.idl" [ attribute ] interface A : interface_base { } 7.2.1 Import 导入,相当于C++的 #include 7.2.2 使用"[]"定义区域,属性描述 关键字 1) object - 后续是对象 2) uuid - 定义对象GUID 3) helpstring - 帮助信息 4) version - 版本 5) point_default - 后续对象 中指针的默认使用方式 比如: uniqune - 表示指针可以 为空,但是不能修改 7.2.3 对象定义 1) 父接口是IUnknown接口 2) 在对象内添加函数,函数定义必须 是返回 HRESULT. HRESULT是32位整数,返回函数是否 执行成功,需要使用 SUCCESSED和 FAILED宏来判断返回值.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值