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

同一进程中两个C++ COM DLL插件如何共享已加载COM DLL并通信?

实现两个COM DLL间共享已加载的COM模块句柄

首先,咱们得明确核心目标:让DLL1全权负责目标COM DLL的加载,DLL2直接复用DLL1已经加载好的模块句柄来调用函数,彻底避免重复加载的问题。下面是一套可落地的实现方案:

1. 在DLL1中封装加载逻辑并暴露句柄获取接口

DLL1要做的是:自己加载目标COM DLL,把句柄存起来,然后给DLL2留一个“获取句柄”的入口。

关键代码实现

在DLL1的某个源文件中添加如下逻辑:

#include <windows.h>
#include <objbase.h>

// 全局变量:保存目标COM DLL的模块句柄(进程内唯一)
static HMODULE g_hTargetComDll = nullptr;
// 引用计数:确保模块在被使用时不会被误卸载
static LONG g_nTargetDllRefCount = 0;

// 初始化目标COM DLL(首次加载或增加引用时调用)
HRESULT InitTargetComDll() {
    if (g_hTargetComDll) {
        // 已加载,直接增加引用计数
        InterlockedIncrement(&g_nTargetDllRefCount);
        return S_OK;
    }

    // 加载目标COM DLL,这里替换成你的实际路径
    g_hTargetComDll = LoadLibraryEx(L"YourTargetCom.dll", nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
    if (!g_hTargetComDll) {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    InterlockedIncrement(&g_nTargetDllRefCount);
    return S_OK;
}

// 释放目标COM DLL(减少引用计数,计数为0时卸载)
void ReleaseTargetComDll() {
    if (g_hTargetComDll && InterlockedDecrement(&g_nTargetDllRefCount) == 0) {
        FreeLibrary(g_hTargetComDll);
        g_hTargetComDll = nullptr;
    }
}

// 导出函数:供DLL2获取目标COM DLL的句柄
extern "C" __declspec(dllexport) HMODULE GetTargetComDllHandle() {
    // 确保模块已加载
    if (SUCCEEDED(InitTargetComDll())) {
        return g_hTargetComDll;
    }
    return nullptr;
}

// 导出函数:供DLL2释放句柄引用
extern "C" __declspec(dllexport) void ReleaseTargetComDllHandle() {
    ReleaseTargetComDll();
}

注意事项

  • 别在DllMain的PROCESS_ATTACH里直接调用InitTargetComDll,DllMain中做复杂操作容易出问题。最好在DLL1首次创建目标COM实例的时候调用InitTargetComDll。
  • 用extern "C"导出函数是为了避免C++的名称修饰,否则DLL2用GetProcAddress会找不到函数。

2. 在DLL2中调用DLL1的接口获取句柄并调用函数

DLL2需要先拿到DLL1的模块句柄,然后调用DLL1导出的函数获取目标COM DLL的句柄,最后用GetProcAddress调用目标函数。

关键代码实现

#include <windows.h>
#include <objbase.h>

// 函数指针定义:匹配DLL1导出的GetTargetComDllHandle
typedef HMODULE(*PFN_GET_TARGET_DLL_HANDLE)();
// 函数指针定义:匹配DLL1导出的ReleaseTargetComDllHandle
typedef void(*PFN_RELEASE_TARGET_DLL_HANDLE)();

// 获取共享的目标COM DLL句柄
HMODULE GetSharedTargetComDllHandle() {
    // 获取DLL1的模块句柄(因为是同一应用的插件,DLL1肯定已加载)
    HMODULE hDll1 = GetModuleHandle(L"DLL1.dll");
    if (!hDll1) {
        return nullptr;
    }

    // 获取DLL1导出的GetTargetComDllHandle函数地址
    PFN_GET_TARGET_DLL_HANDLE pfnGetHandle = reinterpret_cast<PFN_GET_TARGET_DLL_HANDLE>(
        GetProcAddress(hDll1, "GetTargetComDllHandle")
    );
    if (!pfnGetHandle) {
        return nullptr;
    }

    // 调用函数获取句柄
    return pfnGetHandle();
}

// 释放共享句柄的引用
void ReleaseSharedTargetComDllHandle() {
    HMODULE hDll1 = GetModuleHandle(L"DLL1.dll");
    if (!hDll1) {
        return;
    }

    PFN_RELEASE_TARGET_DLL_HANDLE pfnReleaseHandle = reinterpret_cast<PFN_RELEASE_TARGET_DLL_HANDLE>(
        GetProcAddress(hDll1, "ReleaseTargetComDllHandle")
    );
    if (pfnReleaseHandle) {
        pfnReleaseHandle();
    }
}

// 示例:调用目标COM DLL中的函数
HRESULT CallTargetComFunction() {
    HMODULE hTargetDll = GetSharedTargetComDllHandle();
    if (!hTargetDll) {
        return E_FAIL;
    }

    // 替换成你要调用的目标函数的签名和名称
    typedef HRESULT(*PFN_YOUR_TARGET_FUNCTION)(/* 这里填函数参数 */);
    PFN_YOUR_TARGET_FUNCTION pfnTargetFunc = reinterpret_cast<PFN_YOUR_TARGET_FUNCTION>(
        GetProcAddress(hTargetDll, "YourTargetFunctionName")
    );
    if (!pfnTargetFunc) {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        ReleaseSharedTargetComDllHandle();
        return hr;
    }

    // 调用目标函数
    HRESULT hr = pfnTargetFunc(/* 传递参数 */);
    // 释放句柄引用
    ReleaseSharedTargetComDllHandle();
    return hr;
}

3. 额外的COM场景注意

如果你是通过标准CoCreateInstance激活目标COM类,其实COM运行时本身就会在进程级别维护已加载的模块,默认不会重复加载同一CLSID的DLL到同一个进程中(除非用了隔离COM比如Side-by-Side部署)。这种情况下,你完全可以直接在DLL2中调用CoCreateInstance,COM会自动复用已加载的模块,不需要手动共享句柄。

但如果你的场景是手动加载目标COM DLL并直接调用导出函数(不通过COM激活),那上面的手动共享句柄方案就是必需的了。

内容的提问来源于stack exchange,提问作者bhushanblaster

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 14:17:38