源码级 COM 组件复用的一次尝试
2006-12-26 20:26
239 查看
说到 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)
比如已经有了一个 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)
相关文章推荐
- COM---组件复用:包容与聚合
- COM技术内幕学习笔记---第八章---组件复用:包容和聚合
- 终结:Oracle用一次忘一次“尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将”
- [解决]VS2010尝试设置 IDE 的进程内编译器的“References”参数时出现问题。对 COM 组件的调用
- COM技术内幕第八章笔记-组件复用
- Python解析器源码加密系列之(二):一次使用标准c的FILE*访问内存块的尝试
- 通用权限管理系统组件 (GPM - General Permissions Manager) 博客园安家第4年,从卖源码开始尝试卖服务的转变
- vuejs和webpack项目(VueComponent)初尝试——瀑布流组件
- 【转载】COM 组件设计与应用(十一)—— IDispatch 及双接口的调用
- COM 组件设计与应用----起源及复合文件
- 一次发现underscore源码bug的经历以及对学术界『拿来主义』的思考
- 从code.google.com的SVN下载read-only源码的方法
- 从 COM 组件调用 .NET 组件
- 一次失败的动态转换bean的尝试与思考
- .Net提供三种途径供我们导入一个COM组件
- 1、COM组件与.Net组件的比较
- CodeIgniter框架源码分析第一篇comnon.php
- .NET下可复用的TCP通信层实现之TCP组件
- 一次失败的尝试(上):原来GetCustomAttributes方法每次都返回新的实例
- Flume-ng源码解析之Channel组件