轉:com組件純C++建立

轉自:https://blog.csdn.net/shejiannan/article/details/26386271ios

最近在學習COM,本身實現了一個小Demo,拿出來和你們分享一下。求各類批評。windows

我實現的這個組件向外提供了一個接口ICompTest,裏面只有一個函數helloworld(),功能爲返回一個整數89。多線程

實現了自注冊功能。下面貼出代碼,但願對剛開始學習COM的朋友有所幫助。函數

首先看一下工程結構,編譯環境爲vs 2008學習

CompTest工程是服務端工程,是一個dll,CtrlTest是客戶端工程,是一個控制檯工程。this

下面經過客戶端的運行邏輯來說述整個運行流程,先看一下CtrlTest.cpp文件spa

/** * @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。.net

下面看一下接口的聲明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 。code

咱們能夠在 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; } }

其中ULONG g_num表示組件中CompTestClass對象的個數,用於判斷是否能夠卸載本組建,如值爲0則能夠卸載

下面咱們看一下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; }

如下是客戶程序的運行結果

相關文章
相關標籤/搜索