com 包容类实现多个双重接口

来源:互联网 发布:淘宝介入对卖家的影响 编辑:程序博客网 时间:2024/05/27 01:05

有时项目组件X中,包含类CCMyComClsInner 已经实现了接口 ICMyComClsInner 中的虚方法GetInnerData,但是后期项目的发展可能要新增功能,但是又不能影响之前已经存在的功能。因为原来的功能适用于老客户那边的环境,新客户这边的环境需要新的功能实现,所以在不改变现状的情况下,新增接口ICMyComClsInner2并创建新的虚方法GetInnerData2。此方式很类似于微软对IE内核的访问方式 IWebBrowser2 ->Navigate2 以达到新老接口并存的状况,同时也方便后期的维护和移植。

第一步:修改现存接口描述文件IDL,增加新接口  

// 此文件将由 MIDL 工具处理以
// 产生类型库(TestComInner.tlb)和封送处理代码。 
import "oaidl.idl";
import "ocidl.idl";  
[
object,
uuid(a817e7a2-43fa-11d0-9e44-00aa00b6770a),
dual,
helpstring("IComponentRegistrar 接口"),
pointer_default(unique)
]
interface IComponentRegistrar : IDispatch
{
[id(1)] HRESULT Attach([in] BSTR bstrPath);
[id(2)] HRESULT RegisterAll();
[id(3)] HRESULT UnregisterAll();
[id(4)] HRESULT GetComponents([out] SAFEARRAY(BSTR)* pbstrCLSIDs, [out] SAFEARRAY(BSTR)* pbstrDescriptions);
[id(5)] HRESULT RegisterComponent([in] BSTR bstrCLSID);
[id(6)] HRESULT UnregisterComponent([in] BSTR bstrCLSID);
};


[
object,
uuid(2935891B-54B8-4F2B-907E-B4A8E697E360),
dual,
nonextensible,
helpstring("ICMyComClsInner 接口"),
pointer_default(unique)
]
interface ICMyComClsInner : IDispatch{
[id(1), helpstring("方法GetInnerData")] HRESULT GetInnerData([in] LONG value, [out,retval] LONG* retvalue);
}; 

[
object,                                                                           ///代表是接口对象
uuid(5DFDCCBF-773B-4900-9B41-42C63B0D4036),  ///uuid可以通过GUID生成工具自动生成
dual, ///代表此接口是双重接口
nonextensible,
helpstring("ICMyComClsInner2 接口"),
pointer_default(unique)
]
interface ICMyComClsInner2 : IDispatch{
[id(1), helpstring("方法GetInnerData2")] HRESULT GetInnerData2([in] LONG value, [out,retval] LONG* retvalue);
};

 
[
uuid(29E12303-85F0-4359-81F6-03A02E68FEBD),
version(1.0),
helpstring("TestComInner 1.0 类型库"),
custom(a817e7a1-43fa-11d0-9e44-00aa00b6770a,"{6638F0BC-D0A9-4032-969B-A5CFFCF6F58A}")
]
library TestComInnerLib
{
importlib("stdole2.tlb");


[
uuid(6638F0BC-D0A9-4032-969B-A5CFFCF6F58A),
helpstring("ComponentRegistrar 类")
]
coclass CompReg
{
[default] interface IComponentRegistrar;
};
[
uuid(2DD8C6C8-CA25-4398-BA39-872FA9B83CB3),
helpstring("CMyComClsInner Class")
]
coclass CMyComClsInner                                ///coclass 说明是包含类
{
[default] interface ICMyComClsInner;
interface ICMyComClsInner2;                           ///增加新的接口标示
};
};


Interface definition file 是一个接口文件,它应该由用户利用IDL(interface definition language)语言编辑uuidgen产生的IDL原型文件而成。IDL是一种用来说明操作(过程或函数),操作的参数以及数据类型的语言,它在语法上继承于C语言,但形式上和C语言有很多不同,当然也有一些符合自己特点的适合于分布计算的特殊语法。具体可参看DCE RPC specification中的IDL language specification部分。


第二步:修改CCMyComClsInner的头文件CCMyComClsInner.h,增加接口实现和入口点声明

// CCMyComClsInner


class ATL_NO_VTABLE CCMyComClsInner :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CCMyComClsInner, &CLSID_CMyComClsInner>,
public IDispatchImpl<ICMyComClsInner, &IID_ICMyComClsInner, &LIBID_TestComInnerLib, /*wMajor =*/ 1, /*wMinor =*/ 0> ,
public IDispatchImpl<ICMyComClsInner2, &IID_ICMyComClsInner2, &LIBID_TestComInnerLib, /*wMajor =*/ 1, /*wMinor =*/0>
{
public:
CCMyComClsInner()
{

DECLARE_REGISTRY_RESOURCEID(IDR_CMYCOMCLSINNER) 
BEGIN_COM_MAP(CCMyComClsInner)
COM_INTERFACE_ENTRY(ICMyComClsInner)
COM_INTERFACE_ENTRY(ICMyComClsInner2)
//COM_INTERFACE_ENTRY(IDispatch)
////由于是CCMyComClsInner实现了多个双重接口(ICMyComClsInner 和 ICMyComClsInner2),
///所以必须用 COM_INTERFACE_ENTRY2 替换原来的 COM_INTERFACE_ENTRY 来明确声明到底是通过哪一个接口来实现IDispatch的回调响应   

/// 如果不改,会爆出错误: error C2594: “static_cast”: 从“ ::_ComMapClass *”到“IDispatch *
COM_INTERFACE_ENTRY2(IDispatch,ICMyComClsInner)


END_COM_MAP() 
DECLARE_PROTECT_FINAL_CONSTRUCT() 
HRESULT FinalConstruct()
{
return S_OK;

void FinalRelease()
{

public: 
STDMETHOD(GetInnerData)(LONG value, LONG* retvalue); 
virtual HRESULT STDMETHODCALLTYPE GetInnerData2( LONG value, LONG *retvalue ); 
}; 
OBJECT_ENTRY_AUTO(__uuidof(CMyComClsInner), CCMyComClsInner)

 

第三步:在 CCMyComClsInner.cpp文件中完善GetInnerData2方法。

第四步:客户端代码中增加对接口的调用

///直接创建对应组件ICMyComClsInner 接口
ICMyComClsInner *  _IMyComClsInner = NULL;  
HRESULT hr = CoCreateInstance(CLSID_CMyComClsInner, NULL, CLSCTX_ALL, IID_ICMyComClsInner, (void**)&_IMyComClsInner);  
_IMyComClsInner->GetInnerData(123,&_res);
_IMyComClsInner->Release(); 


///直接创建对应组件ICMyComClsInner2接口

ICMyComClsInner2 *  _IMyComClsInner2 = NULL;  

HRESULT hr = CoCreateInstance(CLSID_CMyComClsInner, NULL, CLSCTX_ALL, IID_ICMyComClsInner2, (void**)&_IMyComClsInner2);  
_IMyComClsInner2->GetInnerData2(123,&_res);
_IMyComClsInner->Release(); 


///通过跟接口(IUnknown),查询的方式获取目的组件接口 
IDispatch * _IDispatch =NULL;
_res = 0;
hr = CoCreateInstance(CLSID_CMyComClsInner, NULL, CLSCTX_ALL, IID_IDispatch, (void**)&_IDispatch); 
_IDispatch->QueryInterface(IID_ICMyComClsInner,(void**)&_IMyComClsInner);
_IMyComClsInner->GetInnerData(123,&_res);
_IMyComClsInner->Release();
ICMyComClsInner2 * _IMyComClsInner2 = NULL;  
_IDispatch->QueryInterface(IID_ICMyComClsInner2,(void**)&_IMyComClsInner2);
_IMyComClsInner2->GetInnerData2(123,&_res);
_IMyComClsInner2->QueryInterface(IID_ICMyComClsInner,(void**)&_IMyComClsInner);
_IMyComClsInner->GetInnerData(123,&_res);
_IMyComClsInner2->Release();

0 0