• 转:com组件纯C++创建


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

    最近在学习COM,自己实现了一个小Demo,拿出来和大家分享一下。求各种批评。

    我实现的这个组件向外提供了一个接口ICompTest,里面只有一个函数helloworld(),功能为返回一个整数89。

    实现了自注册功能。下面贴出代码,希望对刚开始学习COM的朋友有所帮助。

    首先看一下工程结构,编译环境为vs 2008

    CompTest工程是服务端工程,是一个dll,CtrlTest是客户端工程,是一个控制台工程。

    下面通过客户端的运行逻辑来讲述整个运行流程,先看一下CtrlTest.cpp文件

    /** 
    *  @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!
    ");  
            return -1;  
        }  
        else{  
            LPOLESTR szCLSID;     
            StringFromCLSID(CLSID_CompTestClass, &szCLSID);     //将其转化为字符串形式用来输出  
            wprintf(L"find CLSID "%s"
    ",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!
    ");  
            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。

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

    我们可以在 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_ROOTCLSID下。

    下面我们看一下这个dll的注册过程。

    用“regsvr32.exe dll路径”对dll进行注册,实际上regsvr32只是调用了dll中的DllRegisterServer引出函数。

    下面我们看一下DllRegisterServer函数的实现,这个函数在CompTest.cpp中。

    int myReg(LPCWSTR lpPath)   //将本组件的信息写入注册表,包括CLSID、所在路径lpPath、ProgID  
    {  
        HKEY thk, tclsidk;  
      
        //打开键HKEY_CLASSES_ROOTCLSID,创建新键为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_ROOTCLSID,创建新键为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;  
    }

    以下是客户程序的运行结果

  • 相关阅读:
    tensorboard以时间命名每一个文件夹
    图像分割loss集合
    博客园使用markdown
    Processed foods make us fatter easily
    python 有4个数字1234,能组成多少个互不相同且无重复的三位数数字。
    python 实现计算器功能 输入字符串,输出相应结果
    解决idea关闭Run窗口时点了Disconnect导致项目一直在跑的问题
    idea导入SpringBoot项目,没有启动按钮,没有maven
    Bean with name 'xxxService' has been injected into other beans [xxxServiceA,xxxServiceB] in its raw version as part of a circular reference, but has eventually been wrapped
    工厂模式
  • 原文地址:https://www.cnblogs.com/feihum/p/11202613.html
Copyright © 2020-2023  润新知