CComObjectRootEx 类
此类提供用于处理非聚合对象和聚合对象的对象引用计数管理的方法。
语法
template<class ThreadModel>
class CComObjectRootEx : public CComObjectRootBase
参数
ThreadModel
其方法实现所需线程模型的类。 可以通过将 ThreadModel 设置为 CComSingleThreadModel、CComMultiThreadModel 或 CComMultiThreadModelNoCS 来显式选择线程模型。 可以通过将 ThreadModel 设置为 CComObjectThreadModel 或 CComGlobalsThreadModel 来接受服务器的默认线程模型。
成员
方法
函数 | 说明 |
---|---|
CComObjectRootEx | 构造函数。 |
InternalAddRef | 递增非聚合对象的引用计数。 |
InternalRelease | 递减非聚合对象的引用计数。 |
锁定 | 如果线程模型是多线程的,则获取临界区对象的所有权。 |
Unlock | 如果线程模型是多线程的,则释放临界区对象的所有权。 |
CComObjectRootBase 方法
函数 | 说明 |
---|---|
FinalConstruct | 在类中重写以执行对象所需的任何初始化。 |
FinalRelease | 在类中重写以执行对象所需的任何清理。 |
OuterAddRef | 递增聚合对象的引用计数。 |
OuterQueryInterface | 委托给聚合对象的外部 IUnknown 。 |
OuterRelease | 递减聚合对象的引用计数。 |
静态函数
函数 | 说明 |
---|---|
InternalQueryInterface | 委托给非聚合对象的 IUnknown 。 |
ObjectMain | 在对对象映射中列出的派生类执行模块初始化和终止期间调用。 |
数据成员
数据成员 | 说明 |
---|---|
m_dwRef | 具有 m_pOuterUnknown ,是并集的一部分。 当对象未聚合为保存 AddRef 和 Release 的引用计数时使用。 |
m_pOuterUnknown | 具有 m_dwRef ,是并集的一部分。 当对象已聚合为保存指向外部未知成员的指针时使用。 |
备注
CComObjectRootEx
处理非聚合对象和聚合对象的对象引用计数管理。 如果未聚合对象,则它会保存对象引用计数;如果正在聚合对象,则它保存指向外部未知成员的指针。 对于聚合对象,可以使用 CComObjectRootEx
方法来处理内部对象构造失败,并在释放内部接口或删除内部对象时防止删除外部对象。
实现 COM 服务器的类必须继承自 CComObjectRootEx
或 CComObjectRoot。
如果类定义指定 DECLARE_POLY_AGGREGATABLE 宏,则 ATL 会在调用 IClassFactory::CreateInstance
时创建 CComPolyObject<CYourClass>
的实例。 在创建过程中,将检查外部未知成员的值。 如果为 NULL,则 IUnknown
为非聚合对象实现。 如果外部未知不为 NULL,则会为聚合对象实现 IUnknown
。
如果类未指定 DECLARE_POLY_AGGREGATABLE 宏,则 ATL 将为聚合对象创建 CAggComObject<CYourClass>
实例,或者为非聚合对象创建 CComObject<CYourClass>
实例。
使用 CComPolyObject
的优点是,避免在模块中同时使用 CComAggObject
和 CComObject
处理聚合和非聚合情况。 单个 CComPolyObject
对象处理这两种情况。 因此模块中只有一个 vtable 副本和一个函数副本。 如果 vtable 很大,这可以大大减小模块大小。 但是,如果 vtable 较小,则使用 CComPolyObject
可能会导致模块大小略大,因为它未针对聚合或非聚合对象进行优化,如 CComAggObject
和 CComObject
所示。
如果对象是聚合的,则 IUnknown 由 CComAggObject
或 CComPolyObject
实现。 这些类将 QueryInterface
、AddRef
和 Release
调用委托给 CComObjectRootEx
的 OuterQueryInterface
、OuterAddRef
和 OuterRelease
以转发到外部未知成员。 通常,你会在类中重写 CComObjectRootEx::FinalConstruct
以创建任何聚合对象,并重写 CComObjectRootEx::FinalRelease
以释放任何聚合对象。
如果对象未聚合,则 IUnknown
由 CComObject
或 CComPolyObject
实现。 在这种情况下,对 QueryInterface
、AddRef
和 Release
的调用将委托给 CComObjectRootEx
的 InternalQueryInterface
、InternalAddRef
和 InternalRelease
以执行实际操作。
要求
标头:atlcom.h
CComObjectRootEx::CComObjectRootEx
该构造函数将引用计数初始化为 0。
CComObjectRootEx();
CComObjectRootEx::FinalConstruct
可以在派生类中重写此方法以执行对象所需的任何初始化。
HRESULT FinalConstruct();
返回值
返回 S_OK(如果成功),或返回标准错误 HRESULT 值之一。
注解
默认情况下,CComObjectRootEx::FinalConstruct
只返回 S_OK。
在 FinalConstruct
而不是类的构造函数中执行初始化可获得以下优势:
无法从构造函数返回状态代码,但可以通过
FinalConstruct
的返回值返回 HRESULT。 使用 ATL 提供的标准类工厂创建类的对象时,此返回值将传播回 COM 客户端,使你能够向对象提供详细的错误信息。无法通过虚拟函数机制从类的构造函数调用虚拟函数。 从类的构造函数调用虚拟函数会导致该函数的静态解析调用,因为它是在继承层次结构中的该位置定义的。 调用纯虚拟函数会导致链接器错误。
类不是继承层次结构中派生程度最高的类 – 它依赖于 ATL 提供的派生类来提供某些功能。 初始化很有可能需要使用该类提供的功能(当类的对象需要聚合其他对象时肯定是这样),但是类中的构造函数无法访问这些功能。 类的构造代码在完全构造派生程度最高的类之前执行。
但是,在完全构造派生程度最高的类之后立即调用
FinalConstruct
,这样就可以调用虚拟函数并使用 ATL 提供的引用计数实现。
示例
通常会在派生自 CComObjectRootEx
的类中重写此方法以创建任何聚合对象。 例如:
class ATL_NO_VTABLE CMyAggObject :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyAggObject, &CLSID_MyAggObject>,
public IDispatchImpl<IMyAggObject, &IID_IMyAggObject, &LIBID_NVC_ATL_COMLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
DECLARE_GET_CONTROLLING_UNKNOWN()
HRESULT FinalConstruct()
{
return CoCreateInstance(CLSID_MyCustomClass, GetControllingUnknown(),
CLSCTX_ALL, IID_IUnknown, (void**)&m_pMyCustomClass);
}
IMyCustomClass* m_pMyCustomClass;
// Remainder of class declaration omitted.
如果构造失败,则可以返回错误。 还可以使用宏 DECLARE_PROTECT_FINAL_CONSTRUCT 来防止删除外部对象,如果在创建期间内部聚合对象递增引用计数,则将计数递减为 0。
下面是创建聚合的典型方式:
将
IUnknown
指针添加到类对象,并将其初始化为构造函数中的 NULL。重写
FinalConstruct
以创建聚合。使用定义为 COM_INTERFACE_ENTRY_AGGREGATE 宏的参数的
IUnknown
指针。重写
FinalRelease
以释放IUnknown
指针。
CComObjectRootEx::FinalRelease
可以在派生类中重写此方法以执行对象所需的任何清理。
void FinalRelease();
注解
默认情况下,CComObjectRootEx::FinalRelease
不执行任何操作。
在 FinalRelease
中执行清理比将代码添加到类的析构函数更可取,因为在调用 FinalRelease
的位置,对象仍会完全构造。 这样就可以安全地访问派生程度最高的类提供的方法。 这对于在删除之前释放任何聚合对象尤其重要。
CComObjectRootEx::InternalAddRef
将非聚合对象的引用计数递增 1。
ULONG InternalAddRef();
返回值
可用于诊断或测试的值。
备注
如果线程模型是多线程的,则使用 InterlockedIncrement
来防止多个线程同时更改引用计数。
CComObjectRootEx::InternalQueryInterface
检索指向所请求的接口的指针。
static HRESULT InternalQueryInterface(
void* pThis,
const _ATL_INTMAP_ENTRY* pEntries,
REFIID iid,
void** ppvObject);
参数
pThis
[in] 指向对象的指针,该对象包含公开给 QueryInterface
的接口的 COM 映射。
pEntries
[in] 指向 _ATL_INTMAP_ENTRY
结构的指针,该结构访问可用接口的映射。
iid
[in] 要请求的接口的 GUID。
ppvObject
[out] 指向 iid 指定的接口指针的指针;如果未找到接口,则为 NULL。
返回值
标准 HRESULT 值之一。
备注
InternalQueryInterface
仅处理 COM 映射表中的接口。 如果对象已聚合,则 InternalQueryInterface
不会委托给外部未知成员。 可以使用 COM_INTERFACE_ENTRY 宏或它的一个变体将接口输入到 COM 映射表。
CComObjectRootEx::InternalRelease
将非聚合对象的引用计数递减 1。
ULONG InternalRelease();
返回值
在非调试和调试版本中,此函数返回一个可能对诊断或测试有用的值。 返回的确切值取决于许多因素(例如使用的操作系统),而不一定取决于引用计数。
备注
如果线程模型是多线程的,则使用 InterlockedDecrement
来防止多个线程同时更改引用计数。
CComObjectRootEx::Lock
如果线程模型是多线程的,则此方法会调用 Win32 API 函数 EnterCriticalSection,该函数会一直等到线程可以拥有通过专用数据成员获取的临界区对象。
void Lock();
备注
当受保护的代码完成执行后,线程必须调用 Unlock
来释放临界区的所有权。
如果线程模型是单线程的,则此方法不执行任何操作。
CComObjectRootEx::m_dwRef
访问四个字节内存的并集的一部分。
long m_dwRef;
备注
具有 m_pOuterUnknown
,是并集的一部分:
union {
long m_dwRef;
IUnknown* m_pOuterUnknown;
};
如果对象未聚合,则 AddRef
和 Release
访问的引用计数存储在 m_dwRef
中。 如果对象已聚合,则指向外部未知成员的指针存储在 m_pOuterUnknown 中。
CComObjectRootEx::m_pOuterUnknown
访问四个字节内存的并集的一部分。
IUnknown*
m_pOuterUnknown;
备注
具有 m_dwRef
,是并集的一部分:
union {
long m_dwRef;
IUnknown* m_pOuterUnknown;
};
如果对象已聚合,则指向外部未知成员的指针存储在 m_pOuterUnknown
中。 如果对象未聚合,则 AddRef
和 Release
访问的引用计数存储在 m_dwRef 中。
CComObjectRootEx::ObjectMain
对于对象映射中列出的每个类,将在模块初始化时调用此函数一次,并在模块终止时再次调用它。
static void WINAPI ObjectMain(bool bStarting);
参数
bStarting
[out] 如果类正在初始化,则值为 TRUE;否则为 FALSE。
备注
bStarting 参数的值指示模块是正在初始化还是已终止。 ObjectMain
的默认实现不执行任何操作,但你可以在类中重写此函数以初始化或清理要为该类分配的资源。 请注意,ObjectMain
将在请求类的任何实例之前调用。
ObjectMain
是从 DLL 的入口点调用的,因此入口点函数可执行的操作类型受到限制。 有关这些限制的详细信息,请参阅 DLL 和 Visual C++ 运行时库行为及 DllMain。
示例
class ATL_NO_VTABLE CMyApp :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyApp, &CLSID_MyApp>,
public IMyApp
{
public:
CMyApp()
{
}
static void WINAPI ObjectMain(bool bStarting)
{
if (bStarting)
;// Perform custom initialization routines
else
;// Perform custom termination routines
}
// Remainder of class declaration omitted.
CComObjectRootEx::OuterAddRef
递增聚合的外部未知成员的引用计数。
ULONG OuterAddRef();
返回值
可用于诊断或测试的值。
CComObjectRootEx::OuterQueryInterface
检索指向所请求接口的间接指针。
HRESULT OuterQueryInterface(REFIID iid, void** ppvObject);
参数
iid
[in] 要请求的接口的 GUID。
ppvObject
[out] 指向 iid 中指定的接口指针的指针;如果聚合不支持该接口,则为 NULL。
返回值
标准 HRESULT 值之一。
CComObjectRootEx::OuterRelease
递减聚合的外部未知成员的引用计数。
ULONG OuterRelease();
返回值
在非调试版本中始终返回 0。 在调试生成中,返回可用于诊断或测试的值。
CComObjectRootEx::Unlock
如果线程模型是多线程的,则此方法会调用 Win32 API 函数 LeaveCriticalSection,该函数释放通过专用数据成员获取的临界区对象。
void Unlock();
备注
若要获取所有权,线程必须调用 Lock
。 每次调用 Lock
都需要相应的调用 Unlock
才能释放临界区的所有权。
如果线程模型是单线程的,则此方法不执行任何操作。