zoukankan      html  css  js  c++  java
  • 创建COM组件全过程

    一.创建一个接口

    struct 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 methods
    
    STDMETHODIMP 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); 
    }
    

    http://baike.baidu.com/view/3883826.htm

    四.创建类厂(必须创建)

    每个类必须配有一个类厂,关注CreateInstance 和LockServer 方法.
    CoCreateInstance方法内部会生命周期依赖这个接口

    //////////////////////////////////////////////////////////////////////
    // IClassFactory methods
    
    STDMETHODIMP 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

    当理解上面流程后再看com技术内幕第七章很很有感觉

    // DllGetClassObject() is called when COM needs to get a class factory.
    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 );
    

    例子参考:

    http://www.codeproject.com/KB/COM/comintro2.aspx

    http://www.vckbase.com/document/viewdoc/?id=213

  • 相关阅读:
    Python--初识函数
    Python中的文件操作
    Python中的集合
    Python中的编码和解码
    Python的关键字is和==
    Python中的字典
    Python中的列表和元组
    Python中几种数据的常用内置方法
    Python的编码
    python_while
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/2047065.html
Copyright © 2011-2022 走看看