如何在C++中为基于第三方IDL的COM服务器实现方法绑定
为COM服务器补充的粘合代码说明
你有C++基础但刚接触COM,要基于给定IDL实现32位COM服务器,当前代码缺少COM核心框架的粘合部分,需要补充以下内容:
1. 改造MyEvent类,实现COM接口
COM组件必须继承并实现IUnknown(以及其派生的IDispatch/IMyEvent),还要维护引用计数。修改后的类定义和实现如下:
类定义
class MyEvent : public IMyEvent { private: long m_cRef; // 引用计数,用于COM对象生命周期管理 public: MyEvent(); ~MyEvent(); // IUnknown 核心方法 HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); ULONG __stdcall AddRef(); ULONG __stdcall Release(); // IDispatch 方法(dispinterface必须实现) HRESULT __stdcall GetTypeInfoCount(UINT* pctinfo); HRESULT __stdcall GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo); HRESULT __stdcall GetIDsOfNames(const IID& riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId); HRESULT __stdcall Invoke(DISPID dispIdMember, const IID& riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr); // 你的自定义方法 SCODE Connect(IDispatch *GetBackToMe); SCODE Disconnect(); };
实现代码
// 构造/析构函数 MyEvent::MyEvent() : m_cRef(1) {} MyEvent::~MyEvent() {} // IUnknown 实现 HRESULT __stdcall MyEvent::QueryInterface(const IID& iid, void** ppv) { // 支持IUnknown、IMyEvent、IDispatch三种接口查询 if (iid == IID_IUnknown || iid == DIID_IMyEvent || iid == IID_IDispatch) { *ppv = static_cast<IMyEvent*>(this); AddRef(); return S_OK; } *ppv = nullptr; return E_NOINTERFACE; } ULONG __stdcall MyEvent::AddRef() { // 原子操作保证多线程安全 return InterlockedIncrement(&m_cRef); } ULONG __stdcall MyEvent::Release() { ULONG refCount = InterlockedDecrement(&m_cRef); if (refCount == 0) delete this; // 引用计数为0时销毁对象 return refCount; } // IDispatch 实现(简化版,仅处理自定义方法) HRESULT __stdcall MyEvent::GetTypeInfoCount(UINT* pctinfo) { *pctinfo = 0; return E_NOTIMPL; } HRESULT __stdcall MyEvent::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { *ppTInfo = nullptr; return E_NOTIMPL; } HRESULT __stdcall MyEvent::GetIDsOfNames(const IID& riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { if (cNames != 1) return DISP_E_UNKNOWNNAME; // 根据方法名映射到对应的DISPID(IDL中定义的id值) if (_wcsicmp(rgszNames[0], L"Connect") == 0) { rgDispId[0] = 1; return S_OK; } else if (_wcsicmp(rgszNames[0], L"Disconnect") == 0) { rgDispId[0] = 2; return S_OK; } return DISP_E_UNKNOWNNAME; } HRESULT __stdcall MyEvent::Invoke(DISPID dispIdMember, const IID& riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { if (wFlags & DISPATCH_METHOD) { switch (dispIdMember) { case 1: // Connect方法 if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_DISPATCH) { SCODE sc = Connect(pDispParams->rgvarg[0].pdispVal); return HRESULT_FROM_SCODE(sc); } return DISP_E_BADPARAMCOUNT; case 2: // Disconnect方法 if (pDispParams->cArgs == 0) { SCODE sc = Disconnect(); return HRESULT_FROM_SCODE(sc); } return DISP_E_BADPARAMCOUNT; default: return DISP_E_MEMBERNOTFOUND; } } return DISP_E_MEMBERNOTFOUND; } // 保留你原有的方法实现 SCODE MyEvent::Connect(IDispatch *GetBackToMe) { // TODO: 实现逻辑 return S_OK; } SCODE MyEvent::Disconnect() { // TODO: 实现逻辑 return S_OK; }
2. 实现类工厂(Class Factory)
COM通过类工厂创建组件实例,需要实现IClassFactory接口:
class MyEventClassFactory : public IClassFactory { private: long m_cRef; public: MyEventClassFactory() : m_cRef(1) {} ~MyEventClassFactory() {} // IUnknown 方法 HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); ULONG __stdcall AddRef(); ULONG __stdcall Release(); // IClassFactory 核心方法 HRESULT __stdcall CreateInstance(IUnknown* pUnkOuter, const IID& iid, void** ppv); HRESULT __stdcall LockServer(BOOL fLock); }; // IUnknown 实现 HRESULT __stdcall MyEventClassFactory::QueryInterface(const IID& iid, void** ppv) { if (iid == IID_IUnknown || iid == IID_IClassFactory) { *ppv = static_cast<IClassFactory*>(this); AddRef(); return S_OK; } *ppv = nullptr; return E_NOINTERFACE; } ULONG __stdcall MyEventClassFactory::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG __stdcall MyEventClassFactory::Release() { ULONG refCount = InterlockedDecrement(&m_cRef); if (refCount == 0) delete this; return refCount; } // IClassFactory 实现 HRESULT __stdcall MyEventClassFactory::CreateInstance(IUnknown* pUnkOuter, const IID& iid, void** ppv) { // COM不支持聚合,所以pUnkOuter必须为null if (pUnkOuter != nullptr) return CLASS_E_NOAGGREGATION; MyEvent* pInstance = new MyEvent(); if (!pInstance) return E_OUTOFMEMORY; // 查询目标接口,然后释放临时引用 HRESULT hr = pInstance->QueryInterface(iid, ppv); pInstance->Release(); return hr; } HRESULT __stdcall MyEventClassFactory::LockServer(BOOL fLock) { // 简单场景下直接返回S_OK,如需控制服务器生命周期可添加计数逻辑 return S_OK; }
3. DLL入口函数(DllMain)
处理DLL加载/卸载时的COM初始化:
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 初始化单线程公寓(STA),如需多线程可改用COINIT_MULTITHREADED CoInitialize(nullptr); break; case DLL_PROCESS_DETACH: CoUninitialize(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; } return TRUE; }
4. 导出COM核心函数
必须导出以下函数,供COM运行时调用:
// 获取类工厂 extern "C" HRESULT __stdcall DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) { if (clsid != CLSID_MyEvent) return CLASS_E_CLASSNOTAVAILABLE; MyEventClassFactory* pFactory = new MyEventClassFactory(); if (!pFactory) return E_OUTOFMEMORY; HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release(); return hr; } // 注册组件到系统注册表 extern "C" HRESULT __stdcall DllRegisterServer() { wchar_t szModulePath[MAX_PATH]; GetModuleFileNameW(nullptr, szModulePath, MAX_PATH); wchar_t szCLSID[40]; StringFromGUID2(CLSID_MyEvent, szCLSID, 40); // 注册CLSID条目 wchar_t szKey[256] = L"CLSID\\"; wcscat_s(szKey, szCLSID); HKEY hKey; if (RegCreateKeyExW(HKEY_CLASSES_ROOT, szKey, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &hKey, nullptr) != ERROR_SUCCESS) return SELFREG_E_CLASS; RegSetValueExW(hKey, nullptr, 0, REG_SZ, (BYTE*)L"MyEvent Event Component", (lstrlenW(L"MyEvent Event Component") + 1) * sizeof(wchar_t)); RegCloseKey(hKey); // 注册InprocServer32(指定DLL路径和线程模型) wcscat_s(szKey, L"\\InprocServer32"); if (RegCreateKeyExW(HKEY_CLASSES_ROOT, szKey, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &hKey, nullptr) != ERROR_SUCCESS) return SELFREG_E_CLASS; RegSetValueExW(hKey, nullptr, 0, REG_SZ, (BYTE*)szModulePath, (lstrlenW(szModulePath) + 1) * sizeof(wchar_t)); RegSetValueExW(hKey, L"ThreadingModel", 0, REG_SZ, (BYTE*)L"Apartment", (lstrlenW(L"Apartment") + 1) * sizeof(wchar_t)); RegCloseKey(hKey); return S_OK; } // 从注册表注销组件 extern "C" HRESULT __stdcall DllUnregisterServer() { wchar_t szCLSID[40]; StringFromGUID2(CLSID_MyEvent, szCLSID, 40); wchar_t szKey[256] = L"CLSID\\"; wcscat_s(szKey, szCLSID); // 删除整个CLSID分支 SHDeleteKeyW(HKEY_CLASSES_ROOT, szKey); return S_OK; } // 可选:判断服务器是否可以卸载 extern "C" HRESULT __stdcall DllCanUnloadNow() { // 如果没有活跃组件实例,返回S_OK;否则返回S_FALSE return S_OK; }
5. 模块定义文件(.def)
创建MyEventServer.def,确保编译器正确导出上述函数:
LIBRARY MyEventServer EXPORTS DllGetClassObject @1 PRIVATE DllRegisterServer @2 PRIVATE DllUnregisterServer @3 PRIVATE DllCanUnloadNow @4 PRIVATE
内容的提问来源于stack exchange,提问作者Stephen
相关产品推荐
相关产品推荐

