WEB开发网
开发学院软件开发VC 用ATL建立轻量级的COM对象(5) 阅读

用ATL建立轻量级的COM对象(5)

 2006-07-21 11:44:30 来源:WEB开发网   
核心提示:第一部分:为什么要使用ATL,第二部分:起步篇,用ATL建立轻量级的COM对象(5),第三部分:实现IUnknown,第四部分:实现接口,某个派生类(CExeModule)必须改写默认的 Lock / Unlock方法,以便以某种适当的方式下掉服务器,不要过分抽象ATL最不直观的一个方面是你所定义和实现的C++类仍然是

第一部分:为什么要使用ATL。

第二部分:起步篇。

第三部分:实现IUnknown。

第四部分:实现接口。

不要过分抽象

ATL最不直观的一个方面是你所定义和实现的C++类仍然是抽象基类。没错,在ATL的模板类和宏上辛苦了半天,却仍然得不到一个可以实例化的类。即使你从 CComObjectRootEx 派生,其结果同从一个或更多的ATL接口实现继承一样。从技术上讲,你的对象不提供 IUnknown 三个核心方法(QueryInterface,AddRef 和 Release)的实现。如果你检查现有ATL之前的 COM 实现,如果不是全部,那么也是大多数的方法实现并不在乎这个类是不是被用于COM聚合或tear-off,是不是被用于独立的对象或一个包含在内的数据成员,是不是要作为基于堆的对象或作为全局变量,以及是不是对象存在时,一直要保持服务器运行。为了允许最大限度的灵活性,所有这些方面分别通过ATL家族中的十个类属的 CComObject 之一来说明。参见下表:

类名服务器是否加锁是否代理IUnknown是否删除对象备注
CComObjectYesNoYes常规情况
CComObjectCachedYes(在第二次AddRef之后)NoYes用于通过内部指针控制的对象
CComObjectNoLockNoNoYes用于不控制服务器运行的对象
CComObjectGlobalYes(在第一次AddRef之后)NoNo用于全程变量
CComObjectStackNoNoNo用于不能被增加引用计数的基于堆栈的变量
CComContainedObjectNoYesNo用于MFC风格的嵌套类
CComAggObjectYesYesYes仅用于聚合实现
CComPolyObjectYesYes(如果聚合)Yes用于聚合/非聚合实现
CComTearOffObjectNoYes(仅用于QueryInterface)Yes用于每次请求所创建的tear-offs 
CComCachedTearOffObjectNoYes(通过第二个IUnknown)Yes用于在第一次请求和缓存时所创建的tear-offs

每一个 CComObject 类用派生来提供正确的 QueryInterface,AddRef 和 Release 实现。如果QueryInterface,AddRef 和 Release的语义正确,则所有 CComObject 类用你的类名作为模板参数并创建一个从你的类派生的新类。在这些类中,CComObjectNoLock 是最容易理解的一个类。请看其代码:

template
class CComObjectNoLock : public Base {
public:
     typedef Base _BaseClass;
     CComObjectNoLock(void* = NULL){}
     ~CComObjectNoLock() {m_dwRef = 1L; FinalRelease();}
     STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
     STDMETHOD_(ULONG, Release)() {
         ULONG l = InternalRelease();
         if (l == 0)
             delete this;
         return l;
     }
     STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
     {return _InternalQueryInterface(iid, ppvObject);}
};
template
class CComObject : public Base {
public:
     typedef Base _BaseClass;
     CComObject(void* = NULL) { _Module.Lock(); }
     ~CComObject() {m_dwRef = 1L; FinalRelease(); _Module.Unlock();
     }
     STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
     STDMETHOD_(ULONG, Release)() {
         ULONG l = InternalRelease();
         if (l == 0)
             delete this;
         return l;
     }
     STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
     {return _InternalQueryInterface(iid, ppvObject);}
     static HRESULT WINAPI CreateInstance(CComObject** pp);
};   
   
  它假设你的对象将在堆中分配,也就是说最终调用 Release 时,将触发这个对象的 delete 操作。CComObjectNoLock 假设你的对象不是可聚合的,并且在服务器运行时,对象并不一直存在(因此有后缀 NoLock)。

为了在堆中分配基于 ATL 类的 CPager 实例,只要对这个类名进行 CComObject 模板包装即可: IPager *p = new CComObjectNoLock();
   
  CComObjectNoLock 类从 CPager 派生,并且添加了由 CPager 提供的使用 InternalQueryInterface,InternalAddRef 和 InternalRelease 的 QueryInterface,AddRef 和 Release实现。因为此对象是基于堆的,所以对 delete 的调用将发生在 CComObjectNoLock 类的 Release 实现中,此时InternalRelease 返回零。

CComObject 类通常被用于基于堆的对象,这个对象只要存在,则服务器一直要运行。与许多 CComObject 家族中的其它类一样,CComObject提供了一个全程变量,_Module,它有两个方法,Lock 和 Unlock。这些方法与 MFC 的 AfxOleLockApp 和AfxOleUnLockApp 很相似。CComObject 的构造函数调用_Module 的 Lock 方法,而 CComObject 的析构函数则调用_Module的Unlock 方法。ATL提供了一个 CComModule 类,它以适当的方式为进程内服务器实现了这些方法。当建立进程外服务器时,某个派生类(CExeModule)必须改写默认的 Lock / Unlock方法,以便以某种适当的方式下掉服务器。基于 AppWizard 的 ATL工程自动会有一个类用 PostThreadMessage 终止主线程的消息循环。(待续)

Tags:ATL 建立 轻量级

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接