• 【VS开发】这就是COM组件


    [实例]这就是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 ICouplet
    
    1st Scroll: Study Hard, Work Hard, Make Money More and More
    2nd Scroll: Eat Well, Sleep Well, Have Fun Day by Day
    Top Scroll: Gelievable
    
    Couplet object deleted
    请按任意键继续. . .

        然后修改Couplet::what()方法,让它输出中文,重新生成Couplet.dll。这一步不用重新注册Couplet.dll,因为Couplet.dll的路径没变,CLSID_Couplet也没变。运行客户程序Reader.exe,其结果如下所示:

    CreateCouplet object
    
    Succeededin 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 location
        char szModule[512];
        DWORD dwResult = ::GetModuleFileName(hModule,szModule,sizeof(szModule)/sizeof(char));
        assert(dwResult!=0);
    
        //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);
    
        //Add the CLSID to the registry
        setKeyAndValue(szKey,NULL,szFriendlyName);
    
        //Add the Server filename subkey under the CLSID key
        setKeyAndValue(szKey,"InprocServer32",szModule);
    
        setKeyAndValue(szKey,"ProgID",szProgID);
    
        setKeyAndValue(szKey,"VersionIndependentProgID",szVerIndProgID);
    
        //Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT
        setKeyAndValue(szVerIndProgID,NULL,szFriendlyName);
        setKeyAndValue(szVerIndProgID,"CLSID",szCLSID);
        setKeyAndValue(szVerIndProgID,"CurVer",szProgID);
    
        //Add the versioned ProgID subkey under HKEY_CLASSES_ROOT
        setKeyAndValue(szProgID,NULL,szFriendlyName);
        setKeyAndValue(szProgID,"CLSID",szCLSID);
        return S_OK;
    }
    
    //
    //Remove the component from the register
    //
    HRESULT UnRegisterServer(const CLSID& clsid,           // Class ID
                             const char* szVerIndProgID,   // Programmatic
                             const 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 Key
        lResult = 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 CLSID
        LPOLESTR wszCLSID = NULL;
        HRESULT hr = StringFromCLSID(clsid,&wszCLSID);
        assert(SUCCEEDED(hr));
    
        //Convert from wide characters to non_wide
        wcstombs(szCLSID,wszCLSID,length);
        
        //Free memory
        CoTaskMemFree(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 child
        FILETIME 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<<"
    上联:我爱的人名花有主
    下联:爱我的人惨不忍睹
    横批:命苦
    
    "; 
            cout<<"
    1st Scroll: Study Hard, Work Hard, Make Money More and More
    
    2nd Scroll: Eat Well, Sleep Well, Have Fun Day by Day
    Top Scroll: Gelievable
    
    "; 
        }
        // constructor
        Couplet() : 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 factory
        CFactory* pFactory = new CFactory;
        if(pFactory == NULL)
        {
            return E_OUTOFMEMORY;
        }
        // Get requested interface
        HRESULT 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
    EXPORTS
        DllCanUnloadNow @1 PRIVATE
        DllGetClassObject @2 PRIVATE
        DllRegisterServer @3 PRIVATE
        DllUnregisterServer @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;
    }
  • 相关阅读:
    第二周作业
    第一次作业
    第0次作业
    第一次的作业
    第0次作业
    第三次作业
    %f使用时的注意事项
    关于c++停止工作
    第二次作业
    第一次作业
  • 原文地址:https://www.cnblogs.com/huty/p/8518743.html
Copyright © 2020-2023  润新知