如何让Native-C模块DLL复用主程序已加载的依赖库?
让Native-C模块DLL复用主程序已有函数的可行方案
方案1:主程序导出共享函数,DLL隐式链接
Windows下EXE和DLL一样支持导出函数,你可以让主程序导出需要共享的工具函数(比如json操作),模块DLL通过导入库隐式链接这些函数,无需重复编译代码。
实现步骤:
主程序导出函数
- 在共享头文件(比如
json.h)中定义导出/导入宏:#ifdef MAIN_EXPORTS #define MAIN_API __declspec(dllexport) #else #define MAIN_API __declspec(dllimport) #endif // 声明需要共享的函数 MAIN_API json_t* json_create(); MAIN_API void json_destroy(json_t* obj); - 主程序编译时,添加预编译宏
MAIN_EXPORTS(比如VS中在项目属性->C/C++->预处理器里添加),编译后会生成主程序的EXE和对应的导入库(主程序名.lib)。
- 在共享头文件(比如
模块DLL链接主程序导入库
- 模块DLL中包含
json.h(此时MAIN_API会自动解析为__declspec(dllimport)),不要编译json.c。 - 将主程序生成的
主程序名.lib添加到DLL的链接依赖中(VS中项目属性->链接器->输入->附加依赖项)。
- 模块DLL中包含
优缺点:
- ✅ 代码改动极小,编译时自动处理依赖,无需手动写加载逻辑
- ✅ 完全复用主程序内存中的函数,DLL体积大幅减小
- ❌ 主程序导出的函数名/签名不能随意修改,否则DLL需要重新编译
方案2:显式绑定主程序函数(灵活适配更新)
如果不想依赖主程序的导入库,或者需要动态适配主程序的函数更新,可以在DLL加载时手动获取主程序的函数地址。
实现步骤:
主程序导出函数
和方案1一样,主程序需要通过__declspec(dllexport)或.def文件导出目标函数。DLL中定义函数指针并绑定
- 在DLL中为每个需要共享的函数定义指针类型:
typedef json_t* (*JsonCreateFunc)(); typedef void (*JsonDestroyFunc)(json_t*); - 定义全局指针变量:
JsonCreateFunc json_create = NULL; JsonDestroyFunc json_destroy = NULL; - 在DLL的初始化入口(比如
DllMain的DLL_PROCESS_ATTACH阶段)获取主程序模块句柄,并绑定函数:BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 获取主程序EXE的模块句柄(当前进程的主模块) HMODULE hMainExe = GetModuleHandle(NULL); if (hMainExe) { json_create = (JsonCreateFunc)GetProcAddress(hMainExe, "json_create"); json_destroy = (JsonDestroyFunc)GetProcAddress(hMainExe, "json_destroy"); } break; // 其他阶段处理... } return TRUE; } - 后续DLL代码中直接使用这些指针变量即可。
- 在DLL中为每个需要共享的函数定义指针类型:
优化技巧:
可以编写脚本(比如Python)遍历共享头文件,自动生成函数指针定义和绑定代码,减少手动重复工作。
优缺点:
- ✅ 主程序函数更新后,只要函数名/签名不变,DLL无需重新编译
- ✅ 无需依赖主程序的导入库,灵活性更高
- ❌ 需要手动维护函数指针和绑定逻辑,代码量略大
方案3:抽离公共工具到独立DLL(最规范的模块化方式)
如果共享函数是通用工具类(比如json操作、日志等),可以将这些代码单独编译成公共DLL,主程序和模块DLL都隐式链接这个公共DLL,从根源避免重复编译。
实现步骤:
创建公共DLL项目
- 将
json.c等公共代码放到独立项目中,编译生成Common.dll和Common.lib,并通过头文件导出函数(方式同方案1的主程序导出)。
- 将
主程序和模块DLL链接公共库
- 主程序和模块DLL都包含公共头文件,链接
Common.lib,直接调用公共函数即可。
- 主程序和模块DLL都包含公共头文件,链接
优缺点:
- ✅ 符合标准模块化设计,代码复用性最强
- ✅ 主程序和模块DLL都无需重复编译公共代码,体积都能减小
- ✅ 公共函数更新时,只需替换
Common.dll,主程序和模块DLL无需重新编译(只要函数签名不变) - ❌ 需要额外维护一个公共DLL项目,但这是长期维护的最优解
关键说明
- 你提到的
*.lib文件可以指向主程序的导出:主程序导出函数后生成的主程序名.lib是导入库,DLL链接它后,加载时会自动从主程序EXE中解析依赖函数。 - 跨模块单例方案本质是通过主程序导出单例对象的指针,模块DLL获取后直接使用,适合共享全局实例,但对于通用函数复用,上面的方案更直接。
内容的提问来源于stack exchange,提问作者Stryker2k2
相关产品推荐
相关产品推荐

