CComObjectRootEx 类

此类提供用于处理非聚合对象和聚合对象的对象引用计数管理的方法。

语法

template<class ThreadModel>
class CComObjectRootEx : public CComObjectRootBase

参数

ThreadModel
其方法实现所需线程模型的类。 可以通过将 ThreadModel 设置为 CComSingleThreadModelCComMultiThreadModelCComMultiThreadModelNoCS 来显式选择线程模型。 可以通过将 ThreadModel 设置为 CComObjectThreadModelCComGlobalsThreadModel 来接受服务器的默认线程模型

成员

方法

函数 说明
CComObjectRootEx 构造函数。
InternalAddRef 递增非聚合对象的引用计数。
InternalRelease 递减非聚合对象的引用计数。
锁定 如果线程模型是多线程的,则获取临界区对象的所有权。
Unlock 如果线程模型是多线程的,则释放临界区对象的所有权。

CComObjectRootBase 方法

函数 说明
FinalConstruct 在类中重写以执行对象所需的任何初始化。
FinalRelease 在类中重写以执行对象所需的任何清理。
OuterAddRef 递增聚合对象的引用计数。
OuterQueryInterface 委托给聚合对象的外部 IUnknown
OuterRelease 递减聚合对象的引用计数。

静态函数

函数 说明
InternalQueryInterface 委托给非聚合对象的 IUnknown
ObjectMain 在对对象映射中列出的派生类执行模块初始化和终止期间调用。

数据成员

数据成员 说明
m_dwRef 具有 m_pOuterUnknown,是并集的一部分。 当对象未聚合为保存 AddRefRelease 的引用计数时使用。
m_pOuterUnknown 具有 m_dwRef,是并集的一部分。 当对象已聚合为保存指向外部未知成员的指针时使用。

备注

CComObjectRootEx 处理非聚合对象和聚合对象的对象引用计数管理。 如果未聚合对象,则它会保存对象引用计数;如果正在聚合对象,则它保存指向外部未知成员的指针。 对于聚合对象,可以使用 CComObjectRootEx 方法来处理内部对象构造失败,并在释放内部接口或删除内部对象时防止删除外部对象。

实现 COM 服务器的类必须继承自 CComObjectRootExCComObjectRoot

如果类定义指定 DECLARE_POLY_AGGREGATABLE 宏,则 ATL 会在调用 IClassFactory::CreateInstance 时创建 CComPolyObject<CYourClass> 的实例。 在创建过程中,将检查外部未知成员的值。 如果为 NULL,则 IUnknown 为非聚合对象实现。 如果外部未知不为 NULL,则会为聚合对象实现 IUnknown

如果类未指定 DECLARE_POLY_AGGREGATABLE 宏,则 ATL 将为聚合对象创建 CAggComObject<CYourClass> 实例,或者为非聚合对象创建 CComObject<CYourClass> 实例。

使用 CComPolyObject 的优点是,避免在模块中同时使用 CComAggObjectCComObject 处理聚合和非聚合情况。 单个 CComPolyObject 对象处理这两种情况。 因此模块中只有一个 vtable 副本和一个函数副本。 如果 vtable 很大,这可以大大减小模块大小。 但是,如果 vtable 较小,则使用 CComPolyObject 可能会导致模块大小略大,因为它未针对聚合或非聚合对象进行优化,如 CComAggObjectCComObject 所示。

如果对象是聚合的,则 IUnknownCComAggObjectCComPolyObject 实现。 这些类将 QueryInterfaceAddRefRelease 调用委托给 CComObjectRootExOuterQueryInterfaceOuterAddRefOuterRelease 以转发到外部未知成员。 通常,你会在类中重写 CComObjectRootEx::FinalConstruct 以创建任何聚合对象,并重写 CComObjectRootEx::FinalRelease 以释放任何聚合对象。

如果对象未聚合,则 IUnknownCComObjectCComPolyObject 实现。 在这种情况下,对 QueryInterfaceAddRefRelease 的调用将委托给 CComObjectRootExInternalQueryInterfaceInternalAddRefInternalRelease 以执行实际操作。

要求

标头: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;
};

如果对象未聚合,则 AddRefRelease 访问的引用计数存储在 m_dwRef 中。 如果对象已聚合,则指向外部未知成员的指针存储在 m_pOuterUnknown 中。

CComObjectRootEx::m_pOuterUnknown

访问四个字节内存的并集的一部分。

IUnknown*
    m_pOuterUnknown;

备注

具有 m_dwRef,是并集的一部分:

union {
    long m_dwRef;
    IUnknown* m_pOuterUnknown;
};

如果对象已聚合,则指向外部未知成员的指针存储在 m_pOuterUnknown 中。 如果对象未聚合,则 AddRefRelease 访问的引用计数存储在 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 才能释放临界区的所有权。

如果线程模型是单线程的,则此方法不执行任何操作。

另请参阅

CComAggObject 类
CComObject 类
CComPolyObject 类
类概述