如何使 LoadLibrary 在缺少依赖项时显示错误对话框
How to make LoadLibrary to show error dialog when there are missing dependencies
假设我们有两个动态库 libfoo.dll
和 libbar.dll
,假设 libbar.dll
依赖于 libfoo.dll
。此外,我们编译一个可执行文件 test.exe
,它使用 WinAPI 函数 LoadLibrary()
加载我们的 libbar.dll
。
如果我们 运行 text.exe
在 Windows XP 上缺少 libfoo.dll
,LoadLibrary()
显示对话框警告而不是 libfoo.dll
实际上丢失并将 LastError
设置为 ERROR_MOD_NOT_FOUND
(126)。
如果我们在 Windows 10 的相同条件下 运行 相同 text.exe
,LoadLibrary()
仅将 LastError
设置为 ERROR_MOD_NOT_FOUND
,无对话框出现。
在这两种情况下,ErrorMode
都是 0。那么是否有可能在 LoadLibrary()
调用过程中捕获缺少依赖项的名称,或者至少如何使 LoadLibrary()
在 Windows 10 上显示错误对话框?
这是一个示例代码(使用 MinGW):
foo.c
int foo(int a, int b)
{
return a + b;
}
编译:gcc foo.c -o libfoo.dll -fPIC -shared
bar.c
int foo(int a, int b);
int bar(int a, int b)
{
return foo(a, b);
}
编译:gcc bar.c -o libbar.dll -fPIC -shared -L. -lfoo
test.c
#include <windows.h>
#include <stdio.h>
typedef int (*pfn)(int a, int b);
int main()
{
SetErrorMode(0);
HMODULE hmod = LoadLibrary("libbar.dll");
if(!hmod)
{
fprintf(stderr, "error loading library %d\n", GetLastError());
return 1;
}
pfn bar = (pfn)GetProcAddress(hmod, "bar");
if(bar)
{
fprintf(stdout, "bar(3, 1) = %d\n", bar(3, 1));
}
else
{
fprintf(stderr, "can't load bar foonction\n");
}
FreeLibrary(hmod);
return 0;
}
编译:gcc test.c -o test
目前看来对提出的问题没有优雅的解决方案。
正如@DavidHeffernan 在对原始 post 的评论中指出的那样,应该在根本不同的层面上解决这个问题。由于 LoadLibrary()
的行为就像它应该的行为一样,关键是正确的安装和错误处理。
但是,如果需要明确捕获动态加载库的缺失依赖项,则可以应用@IInspectable 和@eryksun 提供的技术:
- 为将要动态加载的库启用Delay-Loded DLLs。这种方法为每个依赖模块提供辅助回调,因此可以就地处理缺失的依赖项。这种方法的主要缺点是目标库应该使用适当的链接器标志重新编译;
- 可以编写从应用程序转储调试字符串的辅助实用程序(有关详细信息,请参阅@eryksun 对原始 post 的评论)。缺点:除了需要写一个额外的模块外,它还包括一些注册表操作。
假设我们有两个动态库 libfoo.dll
和 libbar.dll
,假设 libbar.dll
依赖于 libfoo.dll
。此外,我们编译一个可执行文件 test.exe
,它使用 WinAPI 函数 LoadLibrary()
加载我们的 libbar.dll
。
如果我们 运行 text.exe
在 Windows XP 上缺少 libfoo.dll
,LoadLibrary()
显示对话框警告而不是 libfoo.dll
实际上丢失并将 LastError
设置为 ERROR_MOD_NOT_FOUND
(126)。
如果我们在 Windows 10 的相同条件下 运行 相同 text.exe
,LoadLibrary()
仅将 LastError
设置为 ERROR_MOD_NOT_FOUND
,无对话框出现。
在这两种情况下,ErrorMode
都是 0。那么是否有可能在 LoadLibrary()
调用过程中捕获缺少依赖项的名称,或者至少如何使 LoadLibrary()
在 Windows 10 上显示错误对话框?
这是一个示例代码(使用 MinGW):
foo.c
int foo(int a, int b)
{
return a + b;
}
编译:gcc foo.c -o libfoo.dll -fPIC -shared
bar.c
int foo(int a, int b);
int bar(int a, int b)
{
return foo(a, b);
}
编译:gcc bar.c -o libbar.dll -fPIC -shared -L. -lfoo
test.c
#include <windows.h>
#include <stdio.h>
typedef int (*pfn)(int a, int b);
int main()
{
SetErrorMode(0);
HMODULE hmod = LoadLibrary("libbar.dll");
if(!hmod)
{
fprintf(stderr, "error loading library %d\n", GetLastError());
return 1;
}
pfn bar = (pfn)GetProcAddress(hmod, "bar");
if(bar)
{
fprintf(stdout, "bar(3, 1) = %d\n", bar(3, 1));
}
else
{
fprintf(stderr, "can't load bar foonction\n");
}
FreeLibrary(hmod);
return 0;
}
编译:gcc test.c -o test
目前看来对提出的问题没有优雅的解决方案。
正如@DavidHeffernan 在对原始 post 的评论中指出的那样,应该在根本不同的层面上解决这个问题。由于 LoadLibrary()
的行为就像它应该的行为一样,关键是正确的安装和错误处理。
但是,如果需要明确捕获动态加载库的缺失依赖项,则可以应用@IInspectable 和@eryksun 提供的技术:
- 为将要动态加载的库启用Delay-Loded DLLs。这种方法为每个依赖模块提供辅助回调,因此可以就地处理缺失的依赖项。这种方法的主要缺点是目标库应该使用适当的链接器标志重新编译;
- 可以编写从应用程序转储调试字符串的辅助实用程序(有关详细信息,请参阅@eryksun 对原始 post 的评论)。缺点:除了需要写一个额外的模块外,它还包括一些注册表操作。