从应用程序中获取显式加载的 DLL 的符号

Posted

技术标签:

【中文标题】从应用程序中获取显式加载的 DLL 的符号【英文标题】:Getting symbols for explicitly loaded DLL from the application 【发布时间】:2020-05-09 19:29:43 【问题描述】:

我正在修改我的应用程序,以便它可以显式加载 DLL(通过 Windows API)并从 DLL 调用一些函数。但我也需要能够从 DLL 中调用一些应用程序的函数。我只能将函数的头文件提供给 DLL。但我收到 LNK2019 'unresolved external symbol' 错误。

我的想法可行吗?

【问题讨论】:

你能澄清一下吗?您的 exe 是否需要对 DLL 进行任何 隐式 加载(即,它是否需要依赖于链接时间)?如果没有,那么您实际上可以使用 exe 'like' 一个 DLL 并将相关函数导出到 DLL(这将从 EXE 导入它们)。我想。 好的,如果您的 DLL 编译时使用了相关的头文件(对于 EXE 定义的函数),那么这些函数在 EXE 版本中需要为 dll(export),在 DLL 版本中为 dll(import)。要解决链接器错误,您需要将 EXE 的“导出库”添加到 DLL 构建的输入中。我只将它用于 MFC 扩展 DLL,并且它们的加载/分辨率有点不同,所以不能 100% 确定这是可行的。 (如果我向合唱团讲道,请在这里道歉!) 另一种方法是在 DLL 中包含函数指针和虚拟“存根”函数,然后让 EXE 在成功调用 LoadLibrary 后将其真实函数地址发送到 DLL(但如果你有很多这样的功能)。 对于 DLL 项目,VS 会自动生成一个导入库(将其称为导入或导出有点语义 - 取决于您的 POV)。对于 EXE 项目,您必须明确告诉链接器这样做:项目->属性->链接器->高级并指定导入库的位置/名称。您的 DLL 需要与此相关联(在其额外的输入库中,或在代码中的某处使用 #pragma comment(...)。 @Adrian Mole ,我有导出符号的 .exe 项目,并且不需要在项目设置中命名 .lib 文件(我在发表此评论之前检查了一个这样的项目),它已生成与使用 __declspec(dllexport) 的任何二进制文件一样。我没有尝试过使用 .def 或命令行导出的项目,但不希望它们有任何不同。 【参考方案1】:

所以你找到了一种方法来做到这一点,但你也应该问自己是否应该这样做。

如果您的可执行文件依赖于 DLL,那么 DLL 不应依赖于该可执行文件。这不仅仅是一个关于原则的问题,也是一个关于避免未来问题的问题。

实现从 DLL 调用可执行文件的方法是在 DLL 的头文件中定义函数指针类型和设置器:

typedef void aCallbackType(void);
void setMyCallback(aCallbackType* Cb);

setMyCallback 中,DLL 存储该函数指针以供以后调用。

然后可执行文件可以定义一个函数并将其提供给 DLL:

void myCallback(void)  
void myProgram(void) 
    setMyCallback(myCallback);

【讨论】:

我的理解,我可能错了,“DLL 通过 EXE 导出的 EXE 调用”是 nodejs 使用“N-API”动态加载本机模块的方式。我认为它简单、强大且高效。 实际上,我使用此范例来实现 exe 的可选插件模块(请参阅另一个答案中的 cmets)。该插件旨在与该特定 EXE 一起工作,并且可以安全地对 EXE 具有链接时(隐式)依赖(并需要其生成的导入库)。但是,EXE 选择性地(并且显式地)加载 DLL,因此没有链接时依赖性。【参考方案2】:

感谢@AdrianMole 解决了我的问题!要解析 DLL 中的外部符号,您需要创建 EXE 的导入库。在 Visual Studio 2017 中,这可以通过转到项目属性来完成。转到链接器→高级并将导入库设置设置为您想要的路径和名称。 (例如:$(TargetDir)\myimportlibrary.implib)。然后,转到 DLL 项目属性 → 链接器 → 输入并将导入库添加到附加依赖项设置。

【讨论】:

这并没有解决所提出的问题。如前所述,您希望从 显式 加载的 DLL(即使用 LoadLibrary 和朋友加载的模块)导入导出。目前尚不清楚为什么您在实现在运行时严格验证的系统时可能会遇到链接计时器错误。 @IInspectable 好的,我不是 C++ 专业人士,但由于可执行文件和 DLL 是不同的项目,我假设 DLL 在编译时没有所需的符号。 @IInspectable 我认为问题在于 DLL 隐式(在链接时)依赖于 EXE 导出的函数我>。 EXE 的依赖关系不是问题。我对插件模块有这样的安排:EXE 使用LoadLibrary 来加载它们,所以它不需要使用导入库。但是,插件 (DLL) 使用来自 EXE 的函数(实际上是类),因此需要链接到该 EXE 的导入(导出)库。 @adr DLL 可以使用run-time dynamic linking 来访问EXE 导出的服务。尽管接口为循环依赖问题提供了一种更具弹性和可维护性的解决方案。

以上是关于从应用程序中获取显式加载的 DLL 的符号的主要内容,如果未能解决你的问题,请参考以下文章

QT 调用 DLL 方法(三种方法)

如何在程序中用显式加载的方式使用DLL中的导出类

dll加载遇到的问题

动态链接库的加载

无法让 xperfview 加载 DLL 的符号

阻止 Visual Studio 尝试为特定 DLL 加载符号