用ATL建立轻量级的COM对象(5)
2006-07-21 11:44:30 来源:WEB开发网第一部分:为什么要使用ATL。
第二部分:起步篇。
第三部分:实现IUnknown。
第四部分:实现接口。
不要过分抽象
ATL最不直观的一个方面是你所定义和实现的C++类仍然是抽象基类。没错,在ATL的模板类和宏上辛苦了半天,却仍然得不到一个可以实例化的类。即使你从 CComObjectRootEx 派生,其结果同从一个或更多的ATL接口实现继承一样。从技术上讲,你的对象不提供 IUnknown 三个核心方法(QueryInterface,AddRef 和 Release)的实现。如果你检查现有ATL之前的 COM 实现,如果不是全部,那么也是大多数的方法实现并不在乎这个类是不是被用于COM聚合或tear-off,是不是被用于独立的对象或一个包含在内的数据成员,是不是要作为基于堆的对象或作为全局变量,以及是不是对象存在时,一直要保持服务器运行。为了允许最大限度的灵活性,所有这些方面分别通过ATL家族中的十个类属的 CComObject 之一来说明。参见下表:
类名 | 服务器是否加锁 | 是否代理IUnknown | 是否删除对象 | 备注 |
CComObject | Yes | No | Yes | 常规情况 |
CComObjectCached | Yes(在第二次AddRef之后) | No | Yes | 用于通过内部指针控制的对象 |
CComObjectNoLock | No | No | Yes | 用于不控制服务器运行的对象 |
CComObjectGlobal | Yes(在第一次AddRef之后) | No | No | 用于全程变量 |
CComObjectStack | No | No | No | 用于不能被增加引用计数的基于堆栈的变量 |
CComContainedObject | No | Yes | No | 用于MFC风格的嵌套类 |
CComAggObject | Yes | Yes | Yes | 仅用于聚合实现 |
CComPolyObject | Yes | Yes(如果聚合) | Yes | 用于聚合/非聚合实现 |
CComTearOffObject | No | Yes(仅用于QueryInterface) | Yes | 用于每次请求所创建的tear-offs |
CComCachedTearOffObject | No | Yes(通过第二个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 终止主线程的消息循环。(待续)
- ››建立唯美图片特效,十个jQuery插件分享
- ››建立自己的TextView和ImageView的组合View类
- ››轻量级数据交换格式 JSON轻松入门
- ››建立虚拟专用网 使用SSH保护网络通信
- ››轻量级系统 Lubuntu 10.04 发布
- ››建立一个 Derby 日历,第 3 部分: 使用事务和锁定...
- ››建立一个 Derby 日历,第 3 部分: 使用事务和锁定...
- ››建立一个 Derby 日历,第 1 部分: 理解 JDBC
- ››建立一个 Derby 日历,第 2 部分: 嵌入选项(上)...
- ››建立一个 Derby 日历,第 2 部分: 嵌入选项(下)...
- ››建立 qt for symbian 的开发环境
- ››建立一个 Android 项目的实例
更多精彩
赞助商链接