在运行时动态加载库时出现“未解析的外部符号”错误

Posted

技术标签:

【中文标题】在运行时动态加载库时出现“未解析的外部符号”错误【英文标题】:Error "unresolved external symbol" when library is dynamically loaded at runtime 【发布时间】:2018-04-03 12:00:20 【问题描述】:

我有一个大项目,其中生成了一个可执行文件A.exe(使用 cmake 工具链)以动态加载B.dll在运行时。但是如果没有与B.dll 的编译时链接,我在构建可执行文件时得到“未解析的外部符号”。

项目组织如下:

superproject | |--- build/ |----CMakeLists.txt |--- src/ | |---a : a1.cpp, CMakeLists.txt |---b : b.h, b.cpp, export.def, CMakeLists.txt

文件src/b/b.h

class Machine

    int u;
    static Machine* sing;
public:
    Machine();
    static Machine* GetInstance();
    int answer();
;

文件src/b/b.cpp

#include "b.h"

Machine* Machine::sing;

Machine::Machine() : u(1) 

Machine* Machine::GetInstance()

   if (!sing)
      sing = new Machine();
   return sing;


int Machine::answer() return u + 33; 

Machine* GetMach()

   return Machine::GetInstance();

文件src/a/a1.cpp

#include <windows.h>
#include <iostream>

#include "b.h"

using namespace std;
typedef Machine* (*func_t)();
int main(int argc, char* argv[])

   cout << "hello\n";
   HMODULE lib = LoadLibrary("b.dll");
   if (lib)
   
      void* rawfunc = GetProcAddress(lib, "GetMach");
      if (rawfunc)
      
         func_t func = (func_t)(rawfunc);
         Machine* m = func();
         cout << "The exported number is " << m->answer();
      
      else
      
         cout << "Failed to get the symbol";
      
   
   else
   
      cout << "Failed to load library";
   
   getchar();
   return 0;

文件src/b/export.def

EXPORTS
    GetMach

文件src/b/CMakeLists.txt

file(GLOB_RECURSE SRC_FILES *.cpp)
file(GLOB_RECURSE DEF_FILES *.def)
add_library(b MODULE $SRC_FILES $DEF_FILES)

文件src/a/CMakeLists.txt

file(GLOB_RECURSE SRC_FILES *)
include_directories(../b)
add_executable(A $SRC_FILES)

Visual Studio 抱怨:

error LNK2019: unresolved external symbol "public: int __cdecl Machine::answer(void)" (?answer@Machine@@QEAAHXZ) referenced in function main    C:\....\superproj\build\a\a1.obj    A

为什么会出现这个错误以及如何消除它?

【问题讨论】:

您不需要在A.exeB.dll(及其目标)之间建立任何链接。至于B.dll 中的“未定义符号”,它们应该在(其他)库中定义,链接到Btarget_link_libraries(B C),或者通过A 可执行文件或链接到它的库:target_link_libraries(A D)。请注意,A 提供的用于动态加载库B 的符号应为此目的适当标记(使用dllexport 左右)。 @Tsyvarev 好的,我这样做了,但很抱歉消息是“未解析的外部符号”(错误 LNK2019)也许它不同? 对于Machine::answer方法,它应该从B库中导出并由A可执行文件导入,供A使用。或者,您可以将方法声明为virtual,因此A 将在运行时 解析它,从Machine 实例中提取它,该实例是从func() 调用获得的。这就是我在使用动态加载库时导出类的方式。 @Tsyvarev 非常感谢!!它在声明virtual 时有效。也许你想把这些 cmets 变成一个答案? 我稍微重新表述了这个问题,因此它可能对更多读者有用。如果您认为我删除了有意义的细节,请随时进行编辑。此外,您可以删除您的 cmets,这些 cmets 目前已包含在问题帖子本身中。 【参考方案1】:

从链接器的角度来看,非虚拟方法(Machine::answer)与全局函数(例如,GetMach())相同。

也就是说,为了能够使用这种方法,A 应该:

链接 (target_link_libraries) 与定义方法的库或 在运行时请求方法 (GetProcAddress())。

如果这两种方法看起来都不合适,您可以将方法声明为 virtual

virtual int answer();

所有虚拟方法在运行时解析。链接器不关心虚拟方法的使用。

【讨论】:

以上是关于在运行时动态加载库时出现“未解析的外部符号”错误的主要内容,如果未能解决你的问题,请参考以下文章

Windows 套接字封装 - 未解析的外部符号

错误 LNK2001:未解析的外部符号

错误 LNK2019:未解析的外部符号 opencv

为啥在使用模板时会出现“未解析的外部符号”错误? [复制]

MFC 静态链接未解析的外部符号

使用Boost.Python修复未解析的外部符号