任何使用 COM 的进程都必须对 COM 库进行初始化和取消初始化。 除了是规范之外,COM 还在此库中实现一些重要服务。 COM 库在Microsoft Windows中以一组 DLL 和 EXE(主要是 Ole32.dll 和 Rpcss.exe)提供,其中包括:
少数有助于创建 COM 应用程序的基本功能,包括客户端和服务器。 对于客户端,COM 提供了用于创建对象的基本函数。 对于服务器,COM 提供了公开其对象的方法。
COM 通过该实现定位服务,根据唯一的类标识符(CLSID)确定由哪个服务器实现该类,以及该服务器位于何处。 此服务支持在对象类的标识与实现的打包方式之间引入一层间接层(通常是系统注册表),从而使客户端独立于该打包方式,因为该打包方式未来可能会发生变化。
当对象运行于本地或远程服务器上时,可进行透明的远程过程调用。
允许应用程序控制在其进程中分配内存的方式的标准机制,尤其是需要在合作对象之间传递的内存,以便可以正确释放内存。
若要使用基本 COM 服务,客户端和进程外服务器中执行的所有 COM 线程必须在调用除内存分配调用之外的任何其他 COM 函数之前调用 CoInitialize 或 CoInitializeEx 函数。 CoInitializeEx 取代了另一个函数,并增加了一个参数,使您能够指定线程的线程模型:单线程单元或自由线程。 调用 CoInitialize 只需将线程模型设置为单元线程。
OLE 复合文档应用程序调用 OleInitialize 函数,该函数调用 CoInitializeEx ,并执行复合文档所需的一些初始化。 因此,调用 OleInitialize 的线程不能自由线程。 有关客户端和服务器中的线程处理的信息,请参阅 “进程”、“线程”和“单元”。
进程内服务器不会调用初始化函数,因为它们正在加载到已执行此操作的进程中。 因此,进程内服务器必须在 注册表中的 InprocServer32 密钥下设置其线程模型。 有关进程内服务器中的线程问题的详细信息,请参阅 In-Process 服务器线程问题。
对该库执行反初始化也很重要。 对于对 CoInitialize 或 CoInitializeEx 的每个调用,必须有对 CoUninitialize 的相应调用。 对于 对 OleInitialize 的每个调用,必须有对 OleUninitialize 的相应调用。
进程内服务器可以假定他们正在加载到的进程已经执行了这些步骤。
Important
在调用任何其他 COM 函数之前,使用 COM 的每个线程都必须调用 CoInitializeEx 。 这是最常见的 COM 初始化错误 - 在尚未初始化的线程上调用 CoCreateInstance 或其他 COM API 将失败 CO_E_NOTINITIALIZED (0x800401F0)。
Tip
推荐的模式 — 使用 RAII 保护机制来确保未初始化:
#include <wil/com.h> // Windows Implementation Libraries
// wil::unique_couninitialize_call calls CoUninitialize automatically on scope exit
auto comInit = wil::CoInitializeEx(COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
// ... use COM — CoUninitialize is called when comInit goes out of scope,
// even if an exception is thrown ...
如果 wil 不可用,手动调用 CoUninitialize() — 每个成功的 CoInitializeEx 调用必须恰好对应一个 CoUninitialize 调用:
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr)) return hr;
// ... use COM ...
CoUninitialize();
Important
常见错误:
- 在不初始化当前线程上的 COM 的情况下调用 COM 函数。
-
CoInitializeEx/CoUninitialize调用不匹配(每个成功的 init 恰好需要一个 uninit)。 - 在同一线程中混合使用不同的单元模型(若在调用
CoInitializeEx时使用的模型与之前成功调用的模型不同,将返回RPC_E_CHANGED_MODE)。
相关主题