COM的注册过程并不是必须的
2005-11-26 12:01
369 查看
COM的注册过程并不是必须的
hanlray@gmail.com
Revision: 1.0 Date: 2005/11/26现在做一个C++应用,我不会选择COM技术,因为ISO C++本身就可以做好;退一步说,如果要选择,我也许会选XPCOM,一种在Firefox中采用的技术,比COM简单,而且是开源、跨平台的。然而有时候我们是无法选择的:当你使用的系统或外部API要求传入的参数是一个COM接口时,你就会不得不和COM打上交道。比如,要想使你的应用支持从外面过来的拖拽,需要使用COM提供的一个函数RegisterDragDrop,传入一个IDropTarget的COM接口。按照通常的做法,应该是在一个COM模块里做一个实现该接口的COM类,注册该模块,然后用CoCreateInstance来创建实例。然而这里似乎没有必有把这个实现暴露到整个系统,因为只有调用注册函数(RegisterDragDrop)的模块需要这个实现,注册表里的垃圾实在是太多了;当这个实现只对调用注册函数的模块可见时,并且我们不想和什么apartment扯上关系时,还有必要经过CoCreateInstance吗?实际上RegisterDragDrop需要的只是一个叫做IDropTarget的C++接口(抽象类),我写一个类实现该接口,然后用new创建一个实例传进去难道不可以吗?看起来可以,但问题是怎么实现所有COM接口的父接口IUnknown定义的三个函数?虽然很简单,可一次次的重复实现究竟不是一个好主意;写一个基类?QueryInterface似乎不好处理。也许需要写一个小型的框架?秉着不重复发明轮子的原则,我们还是应该使用ATL:class ATL_NO_VTABLE DropTargetImpl : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<DropTargetImpl>, public IDropTarget { BEGIN_COM_MAP(DropTargetImpl) COM_INTERFACE_ENTRY(IDropTarget) END_COM_MAP() public: DropTargetImpl(); virtual ~DropTargetImpl() {} STDMETHOD(DragEnter)( /* [unique][in] */ IDataObject *pDataObj, /* [in] */ DWORD grfKeyState, /* [in] */ POINTL pt, /* [out][in] */ DWORD *pdwEffect); STDMETHOD(DragOver)( /* [in] */ DWORD grfKeyState, /* [in] */ POINTL pt, /* [out][in] */ DWORD *pdwEffect); STDMETHOD(DragLeave)( void); STDMETHOD(Drop)( /* [unique][in] */ IDataObject *pDataObj, /* [in] */ DWORD grfKeyState, /* [in] */ POINTL pt, /* [out][in] */ DWORD *pdwEffect); };
基类CComObjectRootEx对引用计数提供了基本支持,并提供了一个对象锁,类似Java里的那个Monitor,可以用Lock/Unlock来操作。这里的模板实参用的是CComSingleThreadModel,因为一方面RegisterDragDrop函数并没有要求该对象是线程安全的,另一方面我也不打算在另外的线程调用该对象的方法,在这种模型下,引用计数只是简单的增减,而不是InterlockedIncrement/InterlockedDecrement,同时从Lock和Unlock函数也是什么也不做的。想要一个线程安全的实现也很简单,只要把模板参数换作CComMultiThreadModel,并在适当的地方用Lock和Unlock加锁即可,当然,你需要付出同步的代价。
CComCoClass除了有一些报告错误的函数外,还包含一个重要的函数CreateInstance,这将在下面说明。
BEGIN_COM_MAP/END_COM_MAP当然是不可少的,它构造了一张接口映射表。
这时如果我们想要通过new来创建该类的一个实例,编译器一定会报错,因为这个DropTargetImpl还是一个抽象类,其IUnknown接口的三个函数还未实现;另外一个ATL类CComObjectNoLock实现了这三个函数,有了CComObjectRootEx提供的引用计数功能和BEGIN_COM_MAP/END_COM_MAP提供的接口映射表,其实现也是相当简单的。这样,就可以我们就可以这样写:
ATL::CComObjectNoLock<DropTargetImpl>* target = new ATL::CComObjectNoLock<DropTargetImpl>(); HRESULT hr = RegisterDragDrop(hwnd, target);这里显然不能调用delete来释放对象,COM对象会在引用计数为0时自动释放。不过这里有一个问题:如果RegisterDragDrop没有调用AddRef来增加引用计数(当然这里不太可能,但不排除其他API有这种可能),Release自然不会被调用,这样该对象就不会被释放,即使其引用计数为0。实际上,用这种方式创建的COM对象,其初始的引用计数为0,这本身就有违COM规范。当然可以在new后紧接一个AddRef来达到想要的效果,但似乎过于繁琐;这时我们应该用CComCoClass的CreateInstance,它会来保证创建出的COM对象的正确性。实际上我想,ATL把IUnknown接口的实现从其他部分分离出来,可能就是为了防止直接调用new来创建COM对象而带来的错误。
不知什么原因,总以为应该通过CoCreateInstance来创建COM对象,这样就必然需要一个注册过程,而有时我们并不想要这个注册过程;实际上,这个注册过程并不是必须的,ATL也很好地支持了这一点。
相关文章推荐
- 当你输入信用卡号码的时候,有没有担心输错了而造成损失呢?其实可以不必这么担心,因为并不是一个随便的信用卡号码都是合法的,它必须通过Luhn算法来验证通过。 该校验的过程:1、从卡号最后一位数字开始,逆
- 前几天报道的www.baidu.com.sb果然不是谷歌注册的,现在紧去变成山寨搜索了,而且把google的logo反着来
- 设备注册QC MSM平台显示屏Framebuffer设备注册过程
- Exynos4412 中断驱动开发(一)—— 中断基础及中断的注册过程
- COM注册什么时候需要注册APPID
- W32TM注册time.windows.com作为权威时间同步源
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起(写的很好,这个不是从启动app说的,说的是UI是怎么绘制的)
- ORA-06553: PLS-221: 'HASH_SH1' 不是过程或尚未定义的原因和解决方案
- rocketmq namesrv 第二章注册过程
- 信号槽所用的参数类型,必须是Qt能认识的元类型,否则就要调用Q_DECLARE_METATYPE和qRegisterMetaType进行注册
- curl源码分析(二)协议注册与使用过程
- 注册一个GitHub用户的过程
- [SqlServer]用户注册、登录、修改密码存储过程实例
- TWL6030 电源管理芯片中断注册,处理过程
- com.microsoft.sqlserver.jdbc.SQLServerException: 不支持此服务器版本。目标服务器必须是 SQL Server 2000 或更高版本。
- 无法导入reg:指定的文件不是注册脚本——兼了解文本格式问题
- .com和.net域名限价注册六年 com域名优势不减当年
- 用户注册的过程
- COM DLL覆盖更新后需要重新注册
- platform_driver平台驱动注册和注销过程