如何解决调用自定义DLL时出现的未解析外部符号错误?
修复DLL调用的"unresolved external symbol"错误
你的问题出在三个核心点:DLL导入导出宏的错误定义、extern "C"误用在C++类上、静态链接与动态加载方式混用,以下是具体修复方案:
1. 修正DLL导入导出宏
DLL项目和调用项目需要区分导出和导入,用条件编译实现:
修改Header.h的宏定义:
#pragma once #include <string> // 条件编译:DLL项目中定义DLL_EXPORTS(在项目属性-预处理器中添加) #ifdef DLL_EXPORTS #define DLL_API __declspec(dllexport) #else #define DLL_API __declspec(dllimport) #endif // extern "C"仅包裹C风格函数,不要包裹C++类 class DLL_API Config { public: static std::string path; static std::string test; static void get_file(); }; extern "C" DLL_API Config* GetConfigInstance();
注意:在DLL项目的属性 -> C/C++ -> 预处理器 -> 预处理器定义中添加
DLL_EXPORTS,确保编译DLL时用dllexport,调用项目不用加,自动用dllimport。
2. 移除类上的extern "C"
C语言不支持类,extern "C"对C类无效,反而会破坏C的名称修饰,导致链接器找不到类成员的符号。把类从extern "C"块中移出,只保留C风格的导出函数(比如GetConfigInstance)在extern "C"中,避免名称修饰问题。
3. 统一链接方式:二选一
你现在同时用了动态加载(LoadLibrary)和静态链接(依赖.lib),必须统一方式:
方案A:静态链接DLL(推荐,简单直接)
调用项目不需要LoadLibrary,直接通过头文件和.lib链接:
修改main.cpp:
#include <iostream> #include "Header.h" int main() { Config* config = GetConfigInstance(); config->get_file(); std::cout << "img_path: " << Config::path << std::endl; std::cout << "test: " << Config::test << std::endl; delete config; // 记得释放内存,避免泄漏 return 0; }
确保调用项目已配置:
- 包含路径:添加DLL项目的头文件目录
- 库路径:添加DLL项目生成的.lib文件目录
- 链接输入:添加对应的.lib文件名(比如
Util_aoi_model.lib)
方案B:纯动态加载(无需.lib)
如果必须动态加载,需要为类的所有成员函数和静态变量获取函数指针,不能直接通过头文件调用:
步骤1:DLL项目中导出所有需要调用的函数
修改Header.h,添加C风格的导出函数来封装类的操作:
#pragma once #include <string> #ifdef DLL_EXPORTS #define DLL_API __declspec(dllexport) #else #define DLL_API __declspec(dllimport) #endif extern "C" { DLL_API void* GetConfigInstance(); DLL_API void Config_GetFile(void* config); DLL_API const char* Config_GetPath(); DLL_API const char* Config_GetTest(); DLL_API void DestroyConfig(void* config); }
步骤2:DLL项目中实现这些封装函数
修改dllmain.cpp,添加:
// 之前的Config类实现保持不变 void* GetConfigInstance() { return new Config(); } void Config_GetFile(void* config) { static_cast<Config*>(config)->get_file(); } const char* Config_GetPath() { return Config::path.c_str(); } const char* Config_GetTest() { return Config::test.c_str(); } void DestroyConfig(void* config) { delete static_cast<Config*>(config); }
步骤3:调用项目中动态加载所有函数
修改main.cpp:
#include <iostream> #include <windows.h> #include <string> // 定义函数指针类型 typedef void* (*GetConfigInstanceFunc)(); typedef void (*Config_GetFileFunc)(void*); typedef const char* (*Config_GetPathFunc)(); typedef const char* (*Config_GetTestFunc)(); typedef void (*DestroyConfigFunc)(void*); int main() { HINSTANCE hinstLib = LoadLibrary(TEXT("Util_aoi_model.dll")); if (!hinstLib) { std::cerr << "Failed to load DLL" << std::endl; return 1; } // 获取所有函数地址 GetConfigInstanceFunc getInstance = (GetConfigInstanceFunc)GetProcAddress(hinstLib, "GetConfigInstance"); Config_GetFileFunc getFile = (Config_GetFileFunc)GetProcAddress(hinstLib, "Config_GetFile"); Config_GetPathFunc getPath = (Config_GetPathFunc)GetProcAddress(hinstLib, "Config_GetPath"); Config_GetTestFunc getTest = (Config_GetTestFunc)GetProcAddress(hinstLib, "Config_GetTest"); DestroyConfigFunc destroyConfig = (DestroyConfigFunc)GetProcAddress(hinstLib, "DestroyConfig"); if (!getInstance || !getFile || !getPath || !getTest || !destroyConfig) { std::cerr << "Failed to get function addresses" << std::endl; FreeLibrary(hinstLib); return 1; } // 使用DLL功能 void* config = getInstance(); getFile(config); std::cout << "img_path: " << getPath() << std::endl; std::cout << "test: " << getTest() << std::endl; destroyConfig(config); FreeLibrary(hinstLib); return 0; }
4. 额外注意事项
- 确保DLL和调用项目的编译架构一致(都是x86或都是x64),否则会出现链接或加载错误。
- 运行调用项目时,需要把DLL文件放在可执行文件目录下,或者系统路径中。
- 静态成员变量
Config::path和Config::test是全局的,多个实例会共享,注意线程安全问题。
内容的提问来源于stack exchange,提问作者upvup
相关产品推荐
相关产品推荐

