现在的位置: 首页 > 综合 > 正文

源码级 COM 组件复用的一次尝试

2013年05月09日 ⁄ 综合 ⁄ 共 2529字 ⁄ 字号 评论关闭

说到 COM 组件的复用. 那自然就是包容和聚合. 在二进制的层面上的复用. 这次我们要说的是在基于 ATL 的源代码级的复用.
比如已经有了一个 ATL 实现的组件, 比如 CComponentA, 现在想要实现另一个组件 CComponentB. 两个组件要实现的接口都相同, 比如 IComponentA. 而 CComponentB 的行为和 CComponentA 差不多, 只有个别方法的实现有点不同. 如果用 C++ 继承的思想, 应该怎么实现代码的重用呢?

class ATL_NO_VTABLE CComponentA :
 public CComObjectRootEx<CComSingleThreadModel>,
 public CComCoClass<CComponentA, &CLSID_ComponentA>,
 public IComponentA
{..} 
 
注意到 CLSID_ComponentA 是和类相关的. 那么先把它做成模板参数
 
template<const CLSID* pclsid>
class ATL_NO_VTABLE CJunk4 :
 public CComObjectRootEx<CComSingleThreadModel>,
 public CComCoClass<CComponentA, pclsid>,
 public IComponentA
{..}
 
编译自然是不能通过的.
 
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_ComponentA, CComponentA)
END_OBJECT_MAP()
 
这里提示 CComponentA 是模板类. 有办法. 加上这么一个 typedef
 
typedef CComponentA<&CLSID_ComponentA> CComponentA_;
 
然后把 OBJECT_MAP 改成:
OBJECT_ENTRY(CLSID_ComponentA, CComponentA_)
 
编译可以通过了. 那我们现在着手CComponentB 的实现:

class ATL_NO_VTABLE CComponentB :
 public CComponentA<&CLSID_ComponentB>
// public CComObjectRootEx<CComSingleThreadModel>,
// public CComCoClass<CComponentB, &CLSID_ComponentB>,
// public IComponentA
{..}
 
也可以通过编译了. 但是, 用下面的代码测试发现:
 
 CComPtr<IComponentA> sp;

 CoCreateInstance(CLSID_ComponentA, NULL, CLSCTX_INPROC, IID_IComponentA, (void**)&sp);
 sp->Test();
 sp.Release();

 CoCreateInstance(CLSID_ComponentB, NULL, CLSCTX_INPROC, IID_IComponentA, (void**)&sp);
 sp->Test();

两次调用了同样的 Test() 函数!?
 
经过分析, 发现错误出现在最开始的假设. CComponentA 的父类:
 public CComCoClass<CComponentA, &CLSID_ComponentA>,

第一个模板参数是 CComponentA, 这个自然也是和 CComponentA 相关的. 我们需要把它也换成一个模板参数, 象这样:
template<const CLSID* pclsid, class ThisClass>
class ATL_NO_VTABLE CJunk4 :
 public CComObjectRootEx<CComSingleThreadModel>,
 public CComCoClass<ThisClass, pclsid>,
 public IComponentA
{..}
typedef CComponentA<&CLSID_ComponentA, CComponentA> CComponentA_;
 
想法是很好的. 但问题出现在这个 typedef 上, 编译器抱怨说模板参数中的 CComponentA 是模板类, 但没有实例化. 这条路行不通.
查看一下 CComCoClass 类做了些什么:

template <class T, const CLSID* pclsid = &CLSID_NULL>
class CComCoClass
{
public:
        DECLARE_CLASSFACTORY()
        DECLARE_AGGREGATABLE(T)
  ..
}

这里只有 DECLARE_AGGREGATABLE(T) 用到了这个模板参数. 展开 DECLARE_AGGREGATABLE(T)
#define DECLARE_AGGREGATABLE(x) public:/
        typedef CComCreator2< CComCreator< CComObject< x > >, CComCreator< CComAggObject< x > > > _CreatorClass;

原来是一个 typedef. 有办法了. 我们自 CComponentB 中再来一个 typedef, 象这样:
class ATL_NO_VTABLE CComponentB :
 public CComponentA<&CLSID_ComponentB>
// public CComObjectRootEx<CComSingleThreadModel>,
// public CComCoClass<CComponentB, &CLSID_ComponentA>,
// public IComponentA
{
public:
 DECLARE_AGGREGATABLE(CComponentB)
 ..
}

父类和子类中出现相同的 typedef, 经测试发现没有冲突.
再次测试, 已经和预期的效果一样了.

现在 CComponentB 继承了 CComponentA 的实现, 重载感兴趣的方法, 用 C++ 的思想重用了代码. 这里用到的方法有点剑走偏锋. 一般不推荐使用. 但可能也有用得着的地方.

下载代码 (vc6)
 

抱歉!评论已关闭.