You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:08:10