您的位置:首页 > 其它

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也很好地支持了这一点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐