您可以拥有多个具有公共 exports/ordinals 并在 运行 时间交换的 DLL 吗?
Can you have multiple DLLs with common exports/ordinals and exchange at run-time?
我正在尝试创建几个 Win32 64 位 DLL (Windows 10),它们具有不同的实现但一致的符号导出。这样做的目的是在构建时 link 使用任何一个,但在部署时可以选择安装任一 DLL 并正确 运行 。我已经在 Linux 上直接实现了这一点,在那里我对 运行-time linking 更加舒适和熟悉。但是在 Windows,我还没有做到这一点,我想知道这是否可能。我正在尝试使用 VS2010 和 VS2019。
假设我有两个库 blah_legacy.dll 和 blah_modern.dll。它们都导出 6 个符号,这些符号是使用库的接口,例如blah_open, blah_read, blah_write, blah_close, blah_control, blah_status.
我可以 link 使用导入库来实现 blah 实现,并且调用每个符号的测试程序可以使用相应的 blah DLL 正确加载和执行。
但是,我还不能在 运行 时切换 DLL。例如,如果我将它重命名为 blah-legacy.dll,我真的应该能够 link 和 blah-legacy.lib 然后 运行 和 blah-modern.dll 吗? (反之亦然。)
我已经解决了基本的文件命名问题并确保可以实际找到所需的 DLL。我仍然收到应用程序启动失败 (0x22)。
我在 DLL 上使用了“objdump -xs”并注意到符号的顺序和它们的序数不同。所以我创建了一个 .def 文件并确保导出的符号在数字、名称和序数方面匹配。仍然没有 - 发生同样的错误。
我显然还没有弄清楚,希望得到一些指导。这真的可能吗?我从哪里开始寻找(哪些工具)来确定下一步要采取的步骤。
是的。
我不经常使用 Visual Studio,但如果您使用 MSYS2,安装一些 MinGW 软件包并更新它们,这种情况就会一直发生。
我的意思是:MSYS2 是 Windows 的开源软件发行版,除其他外,它还提供了一堆本机 Windows 软件包。包管理器 (pacman
) 让您选择系统中要包含的包,它会下载由 MSYS2 开发人员创建的 DLL 和 EXE。当 MSYS2 开发人员更新库时,您可以下载更新的库包,使用该库的所有其他包将自动开始使用新的 DLL。通常这没有问题,因为新库版本将与旧库版本 ABI 兼容。
您不需要使用LoadLibrary
否则会弄乱您的源代码;链接器和操作系统应该能够为您解决这个问题。
例子
这是我与 MSYS2 结合在一起的一个最小示例,展示了它是如何工作的。
文件 foo_legacy.c
代表您的遗留 DLL。我添加了一些额外的符号,因此它不会与现代 DLL 太相似。
__declspec(dllexport) int eoo() {
return 0;
}
__declspec(dllexport) const char * foo_name() {
return "legacy";
}
__declspec(dllexport) int foo_version() {
return 1;
}
__declspec(dllexport) int goo() {
return 0;
}
文件foo_modern.c
表示现代实现:
__declspec(dllexport) const char * foo_name(void);
__declspec(dllexport) int foo_version(void);
int foo_version() {
return 2;
}
const char * foo_name() {
return "modern";
}
文件 main.c
表示使用 foo
API:
的应用程序
#include <stdio.h>
__declspec(dllimport) const char * foo_name(void);
__declspec(dllimport) int foo_version(void);
int main()
{
printf("%s %d\n", foo_name(), foo_version());
}
我的 build.sh 文件是一个 Bash 构建和测试所有内容的脚本:
#!/usr/bin/bash
set -uex
gcc -Wall foo_legacy.c -shared -o foo_legacy.dll
gcc -Wall foo_modern.c -shared -o foo_modern.dll
gcc -Wall -c main.c -I. -o main.o
gcc main.o foo_legacy.dll -o main.exe
./main.exe # output: "legacy 1"
mv foo_modern.dll foo_legacy.dll
./main.exe # output: "modern 2"
rm foo_legacy.dll
./main.exe # fails because foo_legacy.dll is not found
构建脚本运行 main.exe
三次不同的时间,表明它可以使用旧版 DLL,或使用现代 DLL,或者失败,具体取决于 foo_legacy.dll
中安装的内容。
我正在尝试创建几个 Win32 64 位 DLL (Windows 10),它们具有不同的实现但一致的符号导出。这样做的目的是在构建时 link 使用任何一个,但在部署时可以选择安装任一 DLL 并正确 运行 。我已经在 Linux 上直接实现了这一点,在那里我对 运行-time linking 更加舒适和熟悉。但是在 Windows,我还没有做到这一点,我想知道这是否可能。我正在尝试使用 VS2010 和 VS2019。
假设我有两个库 blah_legacy.dll 和 blah_modern.dll。它们都导出 6 个符号,这些符号是使用库的接口,例如blah_open, blah_read, blah_write, blah_close, blah_control, blah_status.
我可以 link 使用导入库来实现 blah 实现,并且调用每个符号的测试程序可以使用相应的 blah DLL 正确加载和执行。
但是,我还不能在 运行 时切换 DLL。例如,如果我将它重命名为 blah-legacy.dll,我真的应该能够 link 和 blah-legacy.lib 然后 运行 和 blah-modern.dll 吗? (反之亦然。)
我已经解决了基本的文件命名问题并确保可以实际找到所需的 DLL。我仍然收到应用程序启动失败 (0x22)。
我在 DLL 上使用了“objdump -xs”并注意到符号的顺序和它们的序数不同。所以我创建了一个 .def 文件并确保导出的符号在数字、名称和序数方面匹配。仍然没有 - 发生同样的错误。
我显然还没有弄清楚,希望得到一些指导。这真的可能吗?我从哪里开始寻找(哪些工具)来确定下一步要采取的步骤。
是的。
我不经常使用 Visual Studio,但如果您使用 MSYS2,安装一些 MinGW 软件包并更新它们,这种情况就会一直发生。
我的意思是:MSYS2 是 Windows 的开源软件发行版,除其他外,它还提供了一堆本机 Windows 软件包。包管理器 (pacman
) 让您选择系统中要包含的包,它会下载由 MSYS2 开发人员创建的 DLL 和 EXE。当 MSYS2 开发人员更新库时,您可以下载更新的库包,使用该库的所有其他包将自动开始使用新的 DLL。通常这没有问题,因为新库版本将与旧库版本 ABI 兼容。
您不需要使用LoadLibrary
否则会弄乱您的源代码;链接器和操作系统应该能够为您解决这个问题。
例子
这是我与 MSYS2 结合在一起的一个最小示例,展示了它是如何工作的。
文件 foo_legacy.c
代表您的遗留 DLL。我添加了一些额外的符号,因此它不会与现代 DLL 太相似。
__declspec(dllexport) int eoo() {
return 0;
}
__declspec(dllexport) const char * foo_name() {
return "legacy";
}
__declspec(dllexport) int foo_version() {
return 1;
}
__declspec(dllexport) int goo() {
return 0;
}
文件foo_modern.c
表示现代实现:
__declspec(dllexport) const char * foo_name(void);
__declspec(dllexport) int foo_version(void);
int foo_version() {
return 2;
}
const char * foo_name() {
return "modern";
}
文件 main.c
表示使用 foo
API:
#include <stdio.h>
__declspec(dllimport) const char * foo_name(void);
__declspec(dllimport) int foo_version(void);
int main()
{
printf("%s %d\n", foo_name(), foo_version());
}
我的 build.sh 文件是一个 Bash 构建和测试所有内容的脚本:
#!/usr/bin/bash
set -uex
gcc -Wall foo_legacy.c -shared -o foo_legacy.dll
gcc -Wall foo_modern.c -shared -o foo_modern.dll
gcc -Wall -c main.c -I. -o main.o
gcc main.o foo_legacy.dll -o main.exe
./main.exe # output: "legacy 1"
mv foo_modern.dll foo_legacy.dll
./main.exe # output: "modern 2"
rm foo_legacy.dll
./main.exe # fails because foo_legacy.dll is not found
构建脚本运行 main.exe
三次不同的时间,表明它可以使用旧版 DLL,或使用现代 DLL,或者失败,具体取决于 foo_legacy.dll
中安装的内容。