COM接口必须用IDL定义并经MIDL生成头文件,实现类须显式实现IUnknown三方法;CoInitializeEx线程模型须与注册表ThreadingModel严格匹配;DllGetClassObject需extern "C"导出且参数类型正确;ATL仅封装不替代COM契约。
COM 接口定义必须用 IDL 编写,不能直接在 C++ 头文件里手写
COM 接口不是普通 C++ 类接口,它的二进制布局、调用约定、内存模型都受 COM 规范硬性约束。你不能用
直接声明然后期望它能被其他语言或进程正确调用。
IDL(Interface Definition Language)是唯一被 Windows SDK 工具链(
)认可的接口定义方式,它生成的
和
文件才包含正确的
继承、
、
等关键信息。
写
,用
关键字定义方法,每个方法返回
运行
生成
和
在 C++ 实现类中继承生成的
,**必须显式实现
、
、
** —— 即使你用 ATL,底层仍是靠这些函数做接口分发
别把
生成的智能指针头文件当成“接口定义”,那只是客户端封装,服务端仍需原始 IDL + 实现
CoInitializeEx 调用时机和线程模型必须匹配组件注册的 ThreadingModel
COM 不是线程安全的黑盒,它依赖你明确告诉系统当前线程的使用模式。如果组件注册表里
是
(常见于 UI 组件),但你在多线程环境里用
初始化,
就会失败或行为异常。
查看注册表:检查
的值,常见为
或
初始化时严格对应:
对应
,
对应
主线程(如 Win32 GUI 线程)几乎总是要
;后台工作线程若需创建 COM 对象,也得按组件要求初始化,不能复用主线程的 COM 环境
忘记调用
会导致资源泄漏,且下次
可能静默失败
导出 DllGetClassObject 必须用 extern "C" 和 __declspec(dllexport),且参数类型不能错
COM DLL 被加载时,宿主(如
)通过固定符号名
查找入口。C++ 名字修饰(name mangling)会让链接器找不到它,参数类型不匹配则导致栈破坏。
C知道
CSDN推出的一款AI技术问答工具
下载
立即学习
“
C++免费学习笔记(深入)
”;
必须写成:
是
,
是
—— 写成
或
值传递会编译通过但运行崩溃
返回前必须用
检查
是否匹配你的组件,否则别人 CoCreate 你的 CLSID 会失败
不要在
里做耗时操作(如初始化全局资源),它可能被频繁调用;延迟到第一次
或对象构造时更稳妥
ATL 简化了样板代码,但绕不开 COM 核心契约
用 ATL 向导生成的
或
确实省去手写
、
等逻辑,但它只是封装,不是替代。很多问题依然源于对 COM 基础规则的忽视。
ATL 类必须继承自
并用
等宏,否则注册表项缺失
必须和 IDL 中定义的接口 UUID 完全一致,否则
返回
ATL 默认用
,如果你的组件要支持多线程调用,得手动改成
并确保所有成员变量线程安全
调试时看
返回值:-2147221164 是
,-2147221008 是
,这些错误码比弹窗更有指向性
COM 的麻烦不在语法,而在每个环节都依赖精确的二进制契约:UUID 匹配、内存生命周期归属、线程上下文一致性、注册表路径拼写。漏掉任意一环,表现都是“接口调用失败”,但原因可能隔了三层抽象。
class IMyInterfacemidl.exe.h.cIUnknown__declspec(uuid(...))STDMETHODCALLTYPEIMyService.idlinterfaceHRESULTmidl /cpp_win32 IMyService.idlIMyService.hIMyService_i.cIMyServiceQueryInterfaceAddRefRelease#importThreadingModelApartmentCoInitializeEx(NULL, COINIT_MULTITHREADED)CoCreateInstanceHKEY_CLASSES_ROOT\CLSID\{...}\InprocServer32\ThreadingModelApartmentFreeCOINIT_APARTMENTTHREADEDApartmentCOINIT_MULTITHREADEDFreeCOINIT_APARTMENTTHREADEDCoUninitialize()CoInitializeExole32.dllDllGetClassObjectextern "C" HRESULT __declspec(dllexport) DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)REFCLSIDconst CLSID&REFIIDconst IID&CLSIDIIDIsEqualGUIDrclsidDllGetClassObjectQueryInterfaceCComObjectCAtlCOMModuleDllGetClassObjectDllRegisterServerCComObjectRootExDECLARE_REGISTRY_RESOURCEIDCOM_INTERFACE_ENTRY(IMyInterface)QueryInterfaceE_NOINTERFACECComSingleThreadModelCComMultiThreadModelCoCreateInstanceCLASS_NOT_REGISTEREDNO_INTERFACE