建立COM組件全過程(C++)

來源:互聯網
上載者:User
 一:建立一個介面

       

typedef  struct  InterfaceInterface ISimpleMsgBox : public IUnknown{    // IUnknown    STDMETHOD_(ULONG, AddRef)() PURE;    STDMETHOD_(ULONG, Release)() PURE;    STDMETHOD(QueryInterface)(REFIID riid, void** ppv) PURE;    // ISimpleMsgBox    STDMETHOD(DoSimpleMsgBox)(HWND hwndParent, BSTR bsMessageText) PURE;};

替代方案:使用idl檔案定義介面,其會產生相關標頭檔,但可讀性很差

二.聲明一個C++類實現該介面

   

class CSimpleMsgBoxImpl : public ISimpleMsgBox  {public:    CSimpleMsgBoxImpl();    virtual ~CSimpleMsgBoxImpl();    // IUnknown    STDMETHOD_(ULONG, AddRef)();    STDMETHOD_(ULONG, Release)();    STDMETHOD(QueryInterface)(REFIID riid, void** ppv);    // ISimpleMsgBox    STDMETHOD(DoSimpleMsgBox)(HWND hwndParent, BSTR bsMessageText);protected:    ULONG m_uRefCount;};

實現部分,只關注QueryInterface 和DoSimpleMsgBox 部分

STDMETHODIMP CSimpleMsgBoxImpl::QueryInterface ( REFIID riid, void** ppv ){HRESULT hrRet = S_OK;    TRACE(">>> SimpleMsgBoxSvr: In CSimpleMsgBoxImpl::QueryInterface()\n" );    // Check that ppv really points to a void*.    if ( IsBadWritePtr ( ppv, sizeof(void*) ))        return E_POINTER;    // Standard QI initialization - set *ppv to NULL.    *ppv = NULL;    // If the client is requesting an interface we support, set *ppv.    if ( InlineIsEqualGUID ( riid, IID_IUnknown ))        {        *ppv = (IUnknown*) this;        TRACE(">>> SimpleMsgBoxSvr: Client QIed for IUnknown\n" );        }    else if ( InlineIsEqualGUID ( riid, __uuidof(ISimpleMsgBox) ))        {        *ppv = (ISimpleMsgBox*) this;        TRACE(">>> SimpleMsgBoxSvr: Client QIed for ISimpleMsgBox\n" );        }    else        {        hrRet = E_NOINTERFACE;        TRACE(">>> SimpleMsgBoxSvr: Client QIed for unsupported interface\n" );        }    // If we're returning an interface pointer, AddRef() it.    if ( S_OK == hrRet )        {        ((IUnknown*) *ppv)->AddRef();        }    return hrRet;}//////////////////////////////////////////////////////////////////////// ISimpleMsgBox methodsSTDMETHODIMP CSimpleMsgBoxImpl::DoSimpleMsgBox ( HWND hwndParent, BSTR bsMessageText ){_bstr_t bsMsg = bsMessageText;LPCTSTR szMsg = (TCHAR*) bsMsg;         // Use _bstr_t to convert the string to ANSI if necessary.    TRACE(">>> SimpleMsgBoxSvr: In CSimpleMsgBoxImpl::DoSimpleMsgBox()\n" );    MessageBox ( hwndParent, szMsg, _T("Simple Message Box"), MB_OK );    return S_OK;}
三.為介面和類指定各自指定一個GUID

 使用VC++擴充方法

struct __declspec(uuid("{7D51904D-1645-4a8c-BDE0-0F4A44FC38C4}")) ISimpleMsgBox;class  __declspec(uuid("{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}")) CSimpleMsgBoxImpl;
這樣便可以使用__uuidof關鍵字了__uuidof使用方法介紹屬於VC++擴充文法

 

class Demo{};class __declspec(uuid("B372C9F6-1959-4650-960D-73F20CD479BA")) Demo; int main( void ){    CLSID clsid = __uuidof(Demo); }

 

四.建立類廠(必須建立)

  每個類必須配有一個類廠,關注CreateInstance 和LockServer 方法.
CoCreateInstance方法內部會生命週期依賴這個介面

//////////////////////////////////////////////////////////////////////// IClassFactory methodsSTDMETHODIMP CSimpleMsgBoxClassFactory::CreateInstance ( IUnknown* pUnkOuter,                                                         REFIID    riid,                                                         void**    ppv ){HRESULT hrRet;CSimpleMsgBoxImpl* pMsgbox;    TRACE(">>> SimpleMsgBoxSvr: In CSimpleMsgBoxClassFactory::CreateInstance()\n" );    // We don't support aggregation, so pUnkOuter must be NULL.    if ( NULL != pUnkOuter )        return CLASS_E_NOAGGREGATION;    // Check that ppv really points to a void*.    if ( IsBadWritePtr ( ppv, sizeof(void*) ))        return E_POINTER;    *ppv = NULL;    // Create a new COM object!    pMsgbox = new CSimpleMsgBoxImpl;    if ( NULL == pMsgbox )        return E_OUTOFMEMORY;    // QI the object for the interface the client is requesting.    hrRet = pMsgbox->QueryInterface ( riid, ppv );    // If the QI failed, delete the COM object since the client isn't able    // to use it (the client doesn't have any interface pointers on the object).    if ( FAILED(hrRet) )        delete pMsgbox;    return hrRet;}STDMETHODIMP CSimpleMsgBoxClassFactory::LockServer ( BOOL fLock ){    // Increase/decrease the DLL ref count, according to the fLock param.    fLock ? g_uDllLockCount++ : g_uDllLockCount--;    TRACE(">>> SimpleMsgBoxSvr: In CSimpleMsgBoxClassFactory::LockServer(), new DLL ref count = %d\n", g_uDllLockCount );        return S_OK;}

 

五.註冊COM組件(手動註冊)

萬事俱備,現在要將其資訊寫入註冊表,

1.實現DllRegisterServer和DllUnregisterServer方法,並將方法聲明為STDAPI,表明屬於匯出函數

// DllRegisterServer() creates the registy entries that tells COM where our // server is located and its threading model.STDAPI DllRegisterServer(){HKEY  hCLSIDKey = NULL, hInProcSvrKey = NULL;LONG  lRet;TCHAR szModulePath [MAX_PATH];TCHAR szClassDescription[] = _T("SimpleMsgBox class");TCHAR szThreadingModel[]   = _T("Apartment");__try    {    // Create a key under CLSID for our COM server.    lRet = RegCreateKeyEx ( HKEY_CLASSES_ROOT, _T("CLSID\\{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}"),                            0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE | KEY_CREATE_SUB_KEY,                            NULL, &hCLSIDKey, NULL );        if ( ERROR_SUCCESS != lRet )         return HRESULT_FROM_WIN32(lRet);    // The default value of the key is a human-readable description of the coclass.    lRet = RegSetValueEx ( hCLSIDKey, NULL, 0, REG_SZ, (const BYTE*) szClassDescription,                           sizeof(szClassDescription) );    if ( ERROR_SUCCESS != lRet )        return HRESULT_FROM_WIN32(lRet);        // Create the InProcServer32 key, which holds info about our coclass.    lRet = RegCreateKeyEx ( hCLSIDKey, _T("InProcServer32"), 0, NULL, REG_OPTION_NON_VOLATILE,                            KEY_SET_VALUE, NULL, &hInProcSvrKey, NULL );    if ( ERROR_SUCCESS != lRet )        return HRESULT_FROM_WIN32(lRet);    // The default value of the InProcServer32 key holds the full path to our DLL.    GetModuleFileName ( g_hinstThisDll, szModulePath, MAX_PATH );    lRet = RegSetValueEx ( hInProcSvrKey, NULL, 0, REG_SZ, (const BYTE*) szModulePath,                            sizeof(TCHAR) * (lstrlen(szModulePath)+1) );    if ( ERROR_SUCCESS != lRet )        return HRESULT_FROM_WIN32(lRet);    // The ThreadingModel value tells COM how it should handle threads in our DLL.    // The concept of apartments is beyond the scope of this article, but for    // simple, single-threaded DLLs, use Apartment.    lRet = RegSetValueEx ( hInProcSvrKey, _T("ThreadingModel"), 0, REG_SZ,                           (const BYTE*) szThreadingModel,                            sizeof(szThreadingModel) );    if ( ERROR_SUCCESS != lRet )        return HRESULT_FROM_WIN32(lRet);    }   __finally    {    if ( NULL != hCLSIDKey )        RegCloseKey ( hCLSIDKey );    if ( NULL != hInProcSvrKey )        RegCloseKey ( hInProcSvrKey );    }    return S_OK;}// DllUnregisterServer() deleted the registy entries that DllRegisterServer() created.STDAPI DllUnregisterServer(){    // Delete our registry entries.  Note that you must delete from the deepest    // key and work upwards, because on NT/2K, RegDeleteKey() doesn't delete     // keys that have subkeys on NT/2K.    RegDeleteKey ( HKEY_CLASSES_ROOT, _T("CLSID\\{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}\\InProcServer32") );    RegDeleteKey ( HKEY_CLASSES_ROOT, _T("CLSID\\{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}") );    return S_OK;}

 

2.定義def檔案

EXPORTS    DllRegisterServer   PRIVATE    DllUnregisterServer PRIVATE    DllGetClassObject   PRIVATE    DllCanUnloadNow     PRIVATE

 

3.編譯通過後使用regsvr32命令,註冊此dll組件

在調用此com組件之前,必須通過cmd命令註冊,很重要,DllRegisterServer將會成為進入點,

 

六.編寫DllGetClassObject

只有通過全域函數DllGetClassObject,才可以建立類廠,這個方法com類庫會去調用,其會根據CLSID返回一個類工廠(一個dll可能會有多個com類)

生命週期(非常重要):

CoCreateInstance->CoGetClassObject->DllGetClassObject->IClassFactory->CreateInstance

STDAPI DllGetClassObject ( REFCLSID rclsid, REFIID riid, void** ppv ){HRESULT hrRet;CSimpleMsgBoxClassFactory* pFactory;    TRACE(">>> SimpleMsgBoxSvr: In DllGetClassObject()\n");    // Check that the client is asking for the CSimpleMsgBoxImpl factory.    if ( !InlineIsEqualGUID ( rclsid, __uuidof(CSimpleMsgBoxImpl) ))        return CLASS_E_CLASSNOTAVAILABLE;    // Check that ppv really points to a void*.    if ( IsBadWritePtr ( ppv, sizeof(void*) ))        return E_POINTER;    *ppv = NULL;    // Construct a new class factory object.    pFactory = new CSimpleMsgBoxClassFactory;    if ( NULL == pFactory )        return E_OUTOFMEMORY;    // AddRef() the factory since we're using it.    pFactory->AddRef();    // QI() the factory for the interface the client wants.    hrRet = pFactory->QueryInterface ( riid, ppv );        // We're done with the factory, so Release() it.    pFactory->Release();    return hrRet;}
七.調用com組件
void DoMsgBoxTest(HWND hMainWnd){ISimpleMsgBox* pIMsgBox;HRESULT hr;    hr = CoCreateInstance ( __uuidof(CSimpleMsgBoxImpl), NULL, CLSCTX_INPROC_SERVER,                            __uuidof(ISimpleMsgBox), (void**) &pIMsgBox );    if ( FAILED(hr) )        return;    pIMsgBox->DoSimpleMsgBox ( hMainWnd, _bstr_t("Hello COM!") );    pIMsgBox->Release();}

以上步驟不可以省略,可以看到建立一個com組件是比較麻煩的一個流程,很容易出錯

或者先常見一個類工廠

HRESULT hr;IClassFactory *pc=NULL;hr=::CoGetClassObject(__uuidof(CSimpleMsgBoxImpl), CLSCTX_INPROC_SERVER,NULL,     IID_IClassFactory, (void**) &pc );

 

 

 

 

 

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.