【VS开发】这就是COM组件
[實例]這就是COM組件
?CSDN博客 原文??http://blog.csdn.net/btwsmile/article/details/7278492 主題?COM技術[實例]這就是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,其結果如下所示:
Create Couplet object Succeeded in getting the pointer to ICouplet1st Scroll: Study Hard, Work Hard, Make Money More and More 2nd Scroll: Eat Well, Sleep Well, Have Fun Day by Day Top Scroll: GelievableCouplet object deleted 請按任意鍵繼續. . .????然后修改Couplet::what()方法,讓它輸出中文,重新生成Couplet.dll。這一步不用重新注冊Couplet.dll,因為Couplet.dll的路徑沒變,CLSID_Couplet也沒變。運行客戶程序Reader.exe,其結果如下所示:
CreateCouplet objectSucceededin getting the pointer to ICouplet上聯:我愛的人名花有主下聯:愛我的人慘不忍睹橫批:命苦Coupletobject deleted請按任意鍵繼續. . .????這個例子證明了COM組件的更新不會對客戶端造成影響,使用COM組件可以實現模塊與客戶代碼徹底脫耦。實驗結束后,在控制臺輸入指令regsvr32 /u Couplet.dll,從注冊表中將dll模塊信息清除。
3. 代碼清單
/* File List: (COM) IFace.h Register.h Register.cpp Couplet.cpp Couple.def* (Client) IFace.h Reader.cpp* date: 2012-02-21* author: ume*/ ///////////////////////////////////////////////////////////////// // IFace.h 接口的聲明,組件ID、接口ID的定義 // #include <ObjBase.h> // interface interface ICouplet : IUnknown {virtual void what() = 0; }; // GUIDs // {03844548-B0B9-4B12-869D-061AAE2E4B7F} static const GUID IID_ICouplet = { 0x3844548, 0xb0b9, 0x4b12, { 0x86, 0x9d, 0x6, 0x1a, 0xae, 0x2e, 0x4b, 0x7f } }; // {26615B48-1D2E-4A40-9C07-AD5B1B48368C} static const GUID CLSID_Couplet = { 0x26615b48, 0x1d2e, 0x4a40, { 0x9c, 0x7, 0xad, 0x5b, 0x1b, 0x48, 0x36, 0x8c } }; ///////////////////////////////////////////////////////////////// // Register.h 注冊函數的聲明 // HRESULT RegisterServer(HMODULE hModule, const CLSID& clsid, const char* szFriendlyName, const char* szVerIndProgID, const char* szProgID); HRESULT UnRegisterServer(const CLSID& clsid, const char* szVerIndProgID, const char* szProgID); ///////////////////////////////////////////////////////////////// // Register.cpp 注冊函數的定義 // 這些函數可重復使用,非本文重點 // #include <objbase.h> #include "Register.h" //set the given key and its value; BOOL setKeyAndValue(const char* pszPath,const char* szSubkey,const char* szValue); //Convert a CLSID into a char string void CLSIDtochar(const CLSID& clsid,char* szCLSID,int length); //Delete szKeyChild and all of its descendents LONG recursiveDeleteKey(HKEY hKeyParent,const char* szKeyChild); //size of a CLSID as a string const int CLSID_STRING_SIZE = 39; //Register the component in the registry HRESULT RegisterServer(HMODULE hModule,const CLSID& clsid,const char* szFriendlyName,const char* szVerIndProgID,const char* szProgID) {//Get the Server locationchar szModule[512];DWORD dwResult = ::GetModuleFileName(hModule,szModule,sizeof(szModule)/sizeof(char));assert(dwResult!=0);//Convert the CLSID into a charchar szCLSID[CLSID_STRING_SIZE];CLSIDtochar(clsid,szCLSID,sizeof(szCLSID));//Build the key CLSID\\{}char szKey[64];strcpy(szKey,"CLSID\\");strcat(szKey,szCLSID);//Add the CLSID to the registrysetKeyAndValue(szKey,NULL,szFriendlyName);//Add the Server filename subkey under the CLSID keysetKeyAndValue(szKey,"InprocServer32",szModule);setKeyAndValue(szKey,"ProgID",szProgID);setKeyAndValue(szKey,"VersionIndependentProgID",szVerIndProgID);//Add the version-independent ProgID subkey under HKEY_CLASSES_ROOTsetKeyAndValue(szVerIndProgID,NULL,szFriendlyName);setKeyAndValue(szVerIndProgID,"CLSID",szCLSID);setKeyAndValue(szVerIndProgID,"CurVer",szProgID);//Add the versioned ProgID subkey under HKEY_CLASSES_ROOTsetKeyAndValue(szProgID,NULL,szFriendlyName);setKeyAndValue(szProgID,"CLSID",szCLSID);return S_OK; }// //Remove the component from the register // HRESULT UnRegisterServer(const CLSID& clsid, // Class IDconst char* szVerIndProgID, // Programmaticconst char* szProgID) // IDs {//Convert the CLSID into a char.char szCLSID[CLSID_STRING_SIZE];CLSIDtochar(clsid,szCLSID,sizeof(szCLSID));//Build the key CLSID\\{}char szKey[64];strcpy(szKey,"CLSID\\");strcat(szKey,szCLSID);//Delete the CLSID key - CLSID\{}LONG lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szKey);assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));//Delete the version-independent ProgID KeylResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szVerIndProgID);assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));//Delete the ProgID key.lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szProgID);assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));return S_OK; } //Convert a CLSID to a char string void CLSIDtochar(const CLSID& clsid,char* szCLSID,int length) {assert(length>=CLSID_STRING_SIZE);//Get CLSIDLPOLESTR wszCLSID = NULL;HRESULT hr = StringFromCLSID(clsid,&wszCLSID);assert(SUCCEEDED(hr));//Convert from wide characters to non_widewcstombs(szCLSID,wszCLSID,length);//Free memoryCoTaskMemFree(wszCLSID); } // Delete a Key and all of its descendents LONG recursiveDeleteKey(HKEY hKeyParent,const char* lpszKeyChild) {//Open the child.HKEY hKeyChild;LONG lRes = RegOpenKeyEx(hKeyParent,lpszKeyChild,0,KEY_ALL_ACCESS,&hKeyChild);if(lRes != ERROR_SUCCESS)return lRes;//Enumerate all of the decendents of this childFILETIME time;char szBuffer[256];DWORD dwSize = 256 ;while(RegEnumKeyEx(hKeyChild,0,szBuffer,&dwSize,NULL,NULL,NULL,&time) == S_OK){//Delete the decendents of this child.lRes = recursiveDeleteKey(hKeyChild,szBuffer);if(lRes != ERROR_SUCCESS){RegCloseKey(hKeyChild);return lRes;}dwSize = 256;}RegCloseKey(hKeyChild);return RegDeleteKey(hKeyParent,lpszKeyChild); }BOOL setKeyAndValue(const char* szKey,const char* szSubkey,const char* szValue) {HKEY hKey;char szKeyBuf[1024];//Copy keyname into buffer.strcpy(szKeyBuf,szKey);//Add subkey name to buffer.if(szSubkey!=NULL){strcat(szKeyBuf,"\\");strcat(szKeyBuf,szSubkey);}// Create and open key and subkey.long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT ,szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS, NULL, &hKey, NULL) ;if (lResult != ERROR_SUCCESS){return FALSE ;}// Set the Value.if (szValue != NULL){RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szValue, strlen(szValue)+1) ;}RegCloseKey(hKey) ;return TRUE ; } ///////////////////////////////////////////////////////////////// // Couplet.cpp 接口的實現 // 本文的重點,尤其是Couplet和CFactory的實現 // #include "IFace.h" #include "Register.h" #include <iostream> using namespace std; // trace void trace(const char* msg) { cout<<msg<<endl; } // global variables HMODULE g_hModule; static long g_cComponents = 0; static long g_cLocks = 0; // Friendly name of component const char g_szFriendlyName[] = "A Couplet"; // Version independent ProgID const char g_szVerIndProgID[] = "Couplet.Test"; // ProgID const char g_szProgID[] = "Couplet.Test.1"; // implementation class Couplet : public ICouplet { public:virtual LRESULT __stdcall QueryInterface(const IID& iid, void** ppv);virtual ULONG __stdcall AddRef(){return ::InterlockedIncrement(&m_cRef);}virtual ULONG __stdcall Release(){if(::InterlockedDecrement(&m_cRef) == 0){delete this;return 0;}return m_cRef;}virtual void what() { //cout<<"\n上聯:我愛的人名花有主\n下聯:愛我的人慘不忍睹\n橫批:命苦\n\n"; cout<<"\n1st Scroll: Study Hard, Work Hard, Make Money More and More\n\ 2nd Scroll: Eat Well, Sleep Well, Have Fun Day by Day\nTop Scroll: Gelievable\n\n"; }// constructorCouplet() : m_cRef(1) {::InterlockedIncrement(&g_cComponents);trace("Create Couplet object"); }// destructor~Couplet(){::InterlockedDecrement(&g_cComponents);trace("Couplet object deleted");} private:long m_cRef; }; // definition of QueryInterface LRESULT __stdcall Couplet::QueryInterface(const IID& iid, void** ppv) {if((iid == IID_IUnknown) || (iid == IID_ICouplet)){*ppv = static_cast<ICouplet*>(this);}else{*ppv = NULL;return E_NOINTERFACE;}static_cast<IUnknown*>(*ppv)->AddRef();return S_OK; } // class CFactory class CFactory : public IClassFactory { public:virtual LRESULT __stdcall QueryInterface(const IID& iid, void** ppv);virtual ULONG __stdcall AddRef(){return ::InterlockedIncrement(&m_cRef);}virtual ULONG __stdcall Release(){if(::InterlockedDecrement(&m_cRef) == 0){delete this;return 0;}return m_cRef;}virtual LRESULT __stdcall CreateInstance(IUnknown* pCmpntOuter,const IID& iid,void** ppv);virtual LRESULT __stdcall LockServer(BOOL bLock); private:long m_cRef; }; // definition of QueryInterface LRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv) {if((iid == IID_IUnknown) || (iid == IID_IClassFactory)){*ppv = static_cast<IClassFactory*>(this);}else{*ppv = NULL;return E_NOINTERFACE;}static_cast<IUnknown*>(*ppv)->AddRef();return S_OK; } // definition of CreateInstance LRESULT __stdcall CFactory::CreateInstance(IUnknown* pCmpntOuter,const IID& iid,void** ppv) {if(pCmpntOuter != NULL){cout<<"No Aggregate in this Class Factory"<<endl;return CLASS_E_NOAGGREGATION;}Couplet* pCouplet = new Couplet;if(pCouplet == NULL)return E_OUTOFMEMORY;HRESULT hr = pCouplet->QueryInterface(iid, ppv);pCouplet->Release();return hr; } // definition of LockServer LRESULT __stdcall CFactory::LockServer(BOOL bLock) {if(bLock){::InterlockedIncrement(&g_cLocks);}else{::InterlockedDecrement(&g_cLocks);}return S_OK; } STDAPI DllCanUnloadNow() {if((g_cComponents == 0) && (g_cLocks == 0)){return S_OK;}else{return S_FALSE;} } // Get class factory STDAPI DllGetClassObject(const CLSID& clsid,const IID& iid,void** ppv) {// Can we create this component?if(clsid != CLSID_Couplet){return CLASS_E_CLASSNOTAVAILABLE;}// Create class factoryCFactory* pFactory = new CFactory;if(pFactory == NULL){return E_OUTOFMEMORY;}// Get requested interfaceHRESULT hr = pFactory->QueryInterface(iid, ppv);pFactory->Release();return hr; } // register and unregister component STDAPI DllRegisterServer() {return RegisterServer(g_hModule,CLSID_Couplet,g_szFriendlyName,g_szVerIndProgID,g_szProgID); } STDAPI DllUnregisterServer() {return UnRegisterServer(CLSID_Couplet,g_szVerIndProgID,g_szProgID); } // dll main BOOL APIENTRY DllMain(HANDLE hModule,DWORD dwReason,void* lpReserved) {if(dwReason == DLL_PROCESS_ATTACH){g_hModule = (HMODULE)hModule;}return TRUE; } ///////////////////////////////////////////////////////////////// // Couplet.def 模塊定義文件 // LIBRARY Couplet.dll EXPORTSDllCanUnloadNow @1 PRIVATEDllGetClassObject @2 PRIVATEDllRegisterServer @3 PRIVATEDllUnregisterServer @4 PRIVATE ///////////////////////////////////////////////////////////////// // Reader.cpp 通過ICouplet接口調用what()方法讀取對聯內容 // 注意: 客戶端的IFace.h與COM組件中的IFace.h完全一樣 // #include <iostream> #include <ObjBase.h> #include "IFace.h" using namespace std; // global function void trace(const char* pMsg){ cout<<pMsg<<endl; } // main function int main() {::CoInitialize(NULL);ICouplet* pICouplet = NULL;HRESULT hr = ::CoCreateInstance(CLSID_Couplet, NULL, CLSCTX_INPROC_SERVER, IID_ICouplet, (void**)&pICouplet);if(SUCCEEDED(hr)){trace("Succeeded in getting the pointer to ICouplet");pICouplet->what();pICouplet->Release();}else {trace("Failed to get the pointer to ICouplet");}::CoUninitialize();system("pause");return 0; }轉載于:https://www.cnblogs.com/huty/p/8518744.html
總結
以上是生活随笔為你收集整理的【VS开发】这就是COM组件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 完美国际服务器修改器,《完美世界国际2》
- 下一篇: amtlib.dll被McAfee删除之