【问题标题】:possible to handle missing dll file gracefully in Delphi application?可以在 Delphi 应用程序中优雅地处理丢失的 dll 文件吗?
【发布时间】:2015-09-28 16:30:59
【问题描述】:

任何人都知道是否可以在 Delphi 应用程序中优雅地测试和处理丢失的 .dll 文件? 例如,我的代码有这个函数声明:

function KFUNC(Arg1, Arg2, Arg3, Arg4: DWord): longint stdcall; external 'KL2DLL32.DLL' name '_KFUNC@16';

...这当然需要在系统上找到 dll 文件 KL2DLL32.DLL,否则我的应用程序将无法启动。 我想知道是否有一些不同的编码方式,所以我的应用程序可以测试 dll 文件的存在,然后进行相应的处理。显然,目标是即使 dll 文件不存在,我的应用程序仍然可以正常启动。 谢谢。

【问题讨论】:

  • 使用 LoadLibrary 获取过程地址。

标签: delphi dll


【解决方案1】:

您的导入导致函数使用所谓的加载时链接或隐式链接进行链接。也就是说,可执行文件包含元数据,告诉操作系统加载程序加载 DLL,然后绑定到您命名的函数。如果此加载时链接过程失败,则无法加载可执行文件。

您有几个选项可以避免加载时链接,从而使您的程序能够灵活应对链接失败。

延迟加载 DLL

delayed 指令添加到您的函数导入中。文档说:

推迟加载包含函数的库 在实际需要该功能的那一刻,附加延迟 导入函数的指令:

function ExternalMethod(const SomeString: PChar): Integer; stdcall; 
  external 'cstyle.dll' delayed;

delayed 确保包含导入函数的库 不是在应用程序启动时加载,而是在第一次调用时加载 到功能上。

该文档包含其他更详细的有用主题,并介绍了如何处理错误:

显式加载和绑定到 DLL

delayed 指令只是让编译器安排显式加载 DLL 的简洁方法。您可以使用 LoadLibraryGetProcAddress 手动执行相同的操作。

  1. 调用LoadLibrary 加载DLL。要么提供 DLL 的完整路径,要么只提供其名称。在后一种情况下,您依赖 DLL 搜索顺序来定位 DLL。对LoadLibrary 的调用会产生一个模块句柄。
  2. 调用GetProcAddress 以获取命名函数指针的地址。您必须提供步骤 1 中的模块句柄。
  3. 调用第 2 步返回的函数指针。
  4. 当不再需要调用该函数时,使用FreeLibrary卸载DLL。

在每个步骤中,您必须检查函数返回值以防出错。 MSDN 文档中的每个 Win32 API 函数都记录了如何处理错误(如上链接)。例如,如果找不到 DLL,则LoadLibrary 返回0。您必须发现这一点并相应地处理后果。

讨论

虽然delayed指令很方便,但我个人没用过。根据我的经验,每当我需要显式链接时,我总是发现我需要一些delayed 没有提供的额外灵活性。也许我的需求很特殊,但如果您发现自己倾向于明确调用 LoadLibraryGetProcAddress,请不要感到惊讶。

例如,就在今天,我发现自己使用了LoadLibraryEx,因为我想传递LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 标志。当您使用delayed 时,这种细粒度控制不可用。

【讨论】:

  • 还应该提到“延迟”指令是在“后来的”Delphi 版本之一中引入的。它在 Delphi 2007 之前不可用(我不知道它是什么时候引入的)。
  • @dummzeuch 根据 Rob 的说法,这是 2010 年。我个人并不认为 2010 是“较晚的 Delphi 版本之一”!
  • 这就是为什么我把“后来”放在引号里。
【解决方案2】:

当这样声明函数时,您的程序对丢失的 DLL 无能为力。操作系统会在您的程序代码的任何位开始执行之前尝试解析导入的 DLL 函数,因此您无法编写任何代码来处理它。

不过,从 Delphi 2010 开始,您可以更改函数的声明以使用新的 延迟加载 功能。将delayed 指令添加到声明的末尾:

function KFUNC(Arg1, Arg2, Arg3, Arg4: DWord): longint stdcall;
  external 'KL2DLL32.DLL' name '_KFUNC@16' delayed;

如果您使用的是较旧的 Delphi 版本,那么您唯一的选择是在运行时加载 DLL 和函数,然后处理错误。

使用延迟的另一个好处是 SetDliNotifyHook2SetDliFailureHook2 函数允许您分配挂钩,以便您可以分别处理运行时加载通知和故障。
因此,如果在运行时找不到给定的 DLL,甚至是给定的函数,您可以记录错误,甚至用另一个 DLL 句柄或函数指针替换它以满足负载。

another question centering on using a DLL only when required 中更详细地讨论了这两个选项。

【讨论】:

  • delayed 功能只是LoadLibrary()GetProcAddress() 函数的编译器/RTL 包装器。使用delayed 的另一个好处是SetDliNotifyHook2()SetDliFailureHook2() 函数允许您分配挂钩,以便您可以分别处理运行时加载通知和故障。因此,如果在运行时找不到给定的 DLL,甚至是给定的函数,您可以记录错误,甚至用另一个 DLL 句柄或函数指针替换它以满足负载。
猜你喜欢
  • 2018-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-16
相关资源
最近更新 更多