用C++编写COM组件

COM组件的编写

        本文提供一个完全用C++实现的进程内(DLL)COM服务器,不要ATL或MFC提供任何支持。用这种方式编写COM对象可以让你深入地洞察到COM处 理进程内服务器的方法以及COM是如何创建类工厂的。利用本文提供的这个简单框架你可以实现很基本的COM组件,如外壳扩展(Shell Extensions)等。如果你在使用过程中发现了任何问题,请将它反馈到vckbase@public.hk.hi.cn。
        以下是用本文所说的方式编写自己的COM对象要经过的步骤:
第一步:写一个头文件,这个头文件包含以下内容:
1、 包含文件comdef.h:

#include <comdef.h>

2、 定义COM服务器的GUID。

_declspec(selectany) GUID CLSID_Mine = { 0xdc186800,0x657f,0x11d4,{0xb0, 0xb5, 0x0, 0x50, 0xba, 0xbf, 0xc9, 0x4}}; 

3、 给出接口的IID以及这个接口要实现的方法定义。到时客户端会用到这个接口的IID和接口的方法。

interface __declspec(uuid("F614FB00-6702-11d4-B0B7-0050BABFC904")) ImyInterface : public IUnknown
{
    STDMETHOD(Square)(long *pVal)PURE;
    STDMETHOD(Cube)(long *pVal)PURE;
};
客户端使用此接口:
HRESULT hr;
ImyInterface *pmine=(0);
hr = CoCreateInstance(CLSID_Mine, // COM 服务器的CLSID
        NULL, //不支持聚合
        CLSCTX_INPROC_SERVER, // 是个DLL
        __uuidof(ImyInterface), // 接口的IID
        (void**)&pmine);

      还有一种方法可以从注册表中获得COM对象的CLSID,就是调用CLSIDFromProgId()函数,不过必须把组件的ProgId传递给这个函数。

第二步:必须为所定义的接口提供实现,本文用的方法是创建一个从接口继承的新类:

// 
// 这个类实现单接口ImyInterface ...
//
class CmyInterface : public CComBase<> ,public InterfaceImpl<ImyInterface>
{
public:
    CmyInterface();
    virtual ~CmyInterface();

    // 我们必须要为QueryInterface 编写代码
    STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv);

    // ImyInterface 接口方法
    STDMETHOD(Square)(long *pVal);
    STDMETHOD(Cube)(long *pVal);
};

        模版类InterfaceImpl<>提供接口引用计数的实现。在此我们可以用多接口继承,那样就能在一个COM组件中实现多个接口。

第三步:在完成这个对象之前,我们还要编写Queryinterface和两个接口方法:

STDMETHODIMP CmyInterface::QueryInterface(REFIID riid,LPVOID *ppv)
{
    *ppv = NULL;
    if(IsEqualIID(riid,IID_IUnknown) IsEqualIID(riid,__uuidof(ImyInterface)))
    {
        // 因为我们从ImyInterface继承,所以要进行强制类型转换
        *ppv = (ImyInterface *) this;

        _AddRef(); // 这个方法从某个基类继承而来
        return S_OK;
    }
    return E_NOINTERFACE;
}

STDMETHODIMP CmyInterface::Square(long *pVal)
{
    long value = *pVal;
    *pVal = value * value;
    return S_OK;
}

STDMETHODIMP CmyInterface::Cube(long *pVal)
{
    long value = *pVal;
    *pVal = value * value * value;
    return S_OK;
}
    注意这里使用了__uuidof(ImyInterface)来获取接口的IID,这是因为我们已经在第一步中将这个接口关联到了某个uuid。

最后一步:

        COM组件的DLLs必须输出一个叫DllGetClassObject的函数。由这个函数为CmyInterface创建类工厂并返回一个对它的引用。然后我们调用CoCreateInstance为进程内COM创建类工厂,接着调用DllGetClassObject。

        这个类工厂有一个方法是CreateInstance,由这个方法创建对象并返回对它的引用。

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
{
    *ppvOut = NULL;
    if (IsEqualIID(rclsid, CLSID_Mine))
    {
        // 为CmyInterface类声明类工厂
        CClassFactory<CmyInterface>
        *pcf = new CClassFactory<CmyInterface>;
        return pcf->QueryInterface(riid,ppvOut);
    }
    return CLASS_E_CLASSNOTAVAILABLE;
}
       在此我们要检查所请求的CLSID是不是CLSID_Mine,如果不是则返回一个错误代码。
       你可能会问在哪里创建实际的CmyInterface类对象,实际上这是由CClassFactory的模板实例来处理的。以下是CClassFatory的实现:
// CSingleCreator 用于单实例类工厂,这个类为多个CreateObject请求返回相同的对象指针..
template<class comObj>
class CSingleCreator
{
protected:
    CSingleCreator():m_pObj(0) {};

    comObj *CreateObject()
    {
        if(!m_pObj)
        {
            m_pObj = new comObj;
        }
        return m_pObj;
    }
    comObj * m_pObj;
};

// CMultiCreator 用于常用类工厂,这个类为每一个CreateObject请求返回新的对象指针..
template<class comObj>
class CMultiCreator
{
protected:
    CMultiCreator():m_pObj(0) {};
    comObj *CreateObject()
    {
        return new comObj;
    }
    comObj * m_pObj;
};

//ClassFactory类实现
// MultiCreator是缺省的类工厂创建者
//这个类实现了接口IclasFactory......
class CClassFactory : public CComBase<>,
    public InterfaceImpl<IClassFactory>,
    public creatorClass
{
public:
    CClassFactory() {};
    virtual ~CClassFactory() {};

    STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv)
    {
        *ppv = NULL;
        if(IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,IID_IClassFactory))
        {
            *ppv = (IClassFactory *) this;
            _AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }

    STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj)
    {
        *ppvObj = NULL;
        if (pUnkOuter)
            return CLASS_E_NOAGGREGATION;
        m_pObj = CreateObject(); // m_pObj 在creatorClass中定义
        if (!m_pObj)
            return E_OUTOFMEMORY;
        HRESULT hr = m_pObj->QueryInterface(riid, ppvObj);
        if(hr != S_OK)
        {
            delete m_pObj;
        }
        return hr;
    }

    STDMETHODIMP LockServer(BOOL)
    {
        return S_OK;    // 未实现
    }
};
        COM调用CreateInstance创建请求的对象,参数riid指的是所请求的接口IID,如果这个对象支持这个接口,则增加它的引用计数并返回对自身的引用。

        关于代码:本文所提出的方法是如何用纯粹的C++编写COM组件的一个大概念。很多方面的细节都省略了。从本文的文字和代码中可以看出用纯C++编写COM组件需要做些什么工作,如果你要用这种方法编写COM组件的话,这些代码只能是抛砖引玉,具体的实现可以在此基础上往下做.......。


COM组件的使用方法

Requirement:
1.创建myCom.dll,该COM只有一个组件,两个接口:

IGetRes--方法Hello(),
IGetResEx--方法HelloEx()

2.在工程中导入组件或类型库

 #import "组件所在目录\myCom.dll" no_namespace
       或
#import "类型库所在目录\myCom.tlb"
using namespace MYCOM;

--Method 1-------------------------------------------------------
CoInitialize(NULL);
CLSID clsid;
CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
CComPtr<IGetRes> pGetRes;//智能指针
pGetRes.CoCreateInstance(clsid);
pGetRes->Hello();
pGetRes.Release();//小心哦!!请看最后的“注意”
CoUninitialize();

--Method 2---------------------------------------------------------

CoInitialize(NULL);
CLSID clsid;
HRESULT hr=CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
IGetRes *ptr;
hr=CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,
                    __uuidof(IGetRes),(LPVOID*)&ptr);
ptr->Hello();
CoUninitialize();

--Method 3--------------------------------------------------------

CoInitialize(NULL);
HRESULT hr;
CLSID clsid;
hr=CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
IGetRes* ptr;
IGetResEx* ptrEx;

//使用CoCreateClassObject创建一个组件(特别是mutilThreads)的多个对象的时候,效率更高.
IClassFactory* p_classfactory;
hr=CoGetClassObject(clsid,CLSCTX_INPROC_SERVER,
                    NULL,IID_IClassFactory,
                    (LPVOID*)&p_classfactory);
p_classfactory->CreateInstance(NULL,__uuidof(IGetRes),(LPVOID*)&ptr);
p_classfactory->CreateInstance(NULL,__uuidof(IGetResEx),(LPVOID*)&ptrEx);
ptr->Hello();
ptrEx->HelloEx();
CoUninitialize();

--Method 4--------------------------------------------------------
       直接从dll中得到DllGetClassObject,接着生成类对象及类实例(这方法可以
使组件不用在注册表里注册,这是最原始的方法,但这样做没什么意义,至少失去了COM
对用户的透明性),不推荐使用.

typedef HRESULT (__stdcall * pfnHello)(REFCLSID,REFIID,void**);
pfnHello fnHello= NULL;
HINSTANCE hdllInst = LoadLibrary("组件所在目录\myCom.dll");
fnHello=(pfnHello)GetProcAddress(hdllInst,"DllGetClassObject");
if (fnHello != 0)
{
    IClassFactory* pcf = NULL;
    HRESULT hr=(fnHello)(CLSID_GetRes,IID_IClassFactory,(void**)&pcf);
    if (SUCCEEDED(hr) && (pcf != NULL))
    {
        IGetRes* pGetRes = NULL;
        hr = pcf->CreateInstance(NULL, IID_IFoo, (void**)&pGetRes);
        if (SUCCEEDED(hr) && (pFoo != NULL))
        {
            pGetRes->Hello();
            pGetRes->Release();
        }
        pcf->Release();
    }
}
FreeLibrary(hdllInst);

--Method 5-------------------------------------------------------
       通过ClassWizard利用类型库生成包装类,不过前提是com组件的接口必须是派生自IDispatch,具体方法:
       调出添加类向导(.NET中),选择类型库中MFC类,打开,选择"文件",选择"myCom.dll"或"myCom.tlb",接下来会出来该myCom中的所有接口,选择你想生成的接口包装类后,向导会自动生成相应的.h文件.这样你就可以在你的MFC中像使用普通类那样使用组件了.

CoInitialize(NULL);
CGetRes getRest;
if (getRest.CreateDispatch("myCom.GetRes") != 0)
{
    getRest.Hello();
    getRest.ReleaseDispatch();
}
CoUninitialize();

--注意--------------------------------------------------------------
       COM中的智能指针实际上是重载了->的类,目的是为了简化引用记数,几不需要程序员显示的调用AddRef()和Release(),但是为什么我们在Method 1中pGetRes.Release(),问题在与,我们的智能指针pGetRes生命周期的结束是在CoUninitialize()之后,CoInitialize所开的套间在CoUninitialize()后已经被关闭,而pGetRes此时发生析构,导致了程序的崩溃,解决这个问题的另一个方法是
CoInitialize(NULL);
CLSID clsid;
CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
{
    CComPtr<IGetRes> pGetRes;//智能指针
    pGetRes.CoCreateInstance(clsid);
    pGetRes->Hello();
}
CoUninitialize();
--------------------------------------------------------------------

        以上就是COM的5种方法,当然具体怎么使用还是在于程序的环境,加以琢磨....

http://www.yesky.com/dev/382/2019882.shtml

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
COMComponent Object Model)是一种面向对象的组件化技术,常用于实现跨进程、跨机器的组件通信。C++可以使用COM组件来实现这种技术。 下面是使用C++创建和使用COM组件的基本步骤: 1.定义组件接口:使用IDL(Interface Definition Language)语言定义组件接口,包括组件的方法、属性和事件等。 2.实现组件:使用C++编写组件的实现代码,实现组件接口中定义的方法、属性和事件等。 3.注册组件:使用Windows系统提供的regsvr32工具将组件注册到系统中,以便其他程序可以使用它。 4.使用组件:在其他程序中通过COM接口调用组件的方法、属性和事件等。 以下是一个简单的示例,演示如何使用C++创建和使用COM组件: 1.定义组件接口: ``` // MyComponent.idl import "oaidl.idl"; [ object, uuid(01234567-89ab-cdef-0123-456789abcdef), dual, helpstring("IMyComponent Interface"), pointer_default(unique) ] interface IMyComponent : IDispatch{ [id(1), helpstring("Method1")] HRESULT Method1([in] BSTR arg1, [out, retval] VARIANT* retVal); }; ``` 2.实现组件: ``` // MyComponent.h class ATL_NO_VTABLE CMyComponent : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CMyComponent, &CLSID_MyComponent>, public IDispatchImpl<IMyComponent, &IID_IMyComponent, &LIBID_MyComponentLib, /*wMajor =*/ 1, /*wMinor =*/ 0> { public: DECLARE_REGISTRY_RESOURCEID(IDR_MYCOMPONENT) DECLARE_NOT_AGGREGATABLE(CMyComponent) BEGIN_COM_MAP(CMyComponent) COM_INTERFACE_ENTRY(IMyComponent) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() // IMyComponent methods public: STDMETHOD(Method1)(BSTR arg1, VARIANT* retVal); }; ``` ``` // MyComponent.cpp STDMETHODIMP CMyComponent::Method1(BSTR arg1, VARIANT* retVal) { // Do something here return S_OK; } ``` 3.注册组件: ``` regsvr32 MyComponent.dll ``` 4.使用组件: ``` // Client.cpp #include "MyComponent.h" int main() { CoInitialize(NULL); IMyComponentPtr pMyComponent; HRESULT hr = pMyComponent.CreateInstance(__uuidof(MyComponent)); if (SUCCEEDED(hr)) { VARIANT ret; hr = pMyComponent->Method1(L"arg1", &ret); if (SUCCEEDED(hr)) { // Do something with the returned VARIANT } } CoUninitialize(); return 0; } ``` 以上是一个简单的示例,演示了如何使用C++创建和使用COM组件。实际应用中,还需要考虑其他方面的问题,例如线程安全、错误处理等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值