可以在Delphi应用程序中优雅地处理丢失的dll文件?

chi*_*son 6 delphi dll

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

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

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

Dav*_*nan 11

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

您有两个选项可以避免加载时链接,从而使您的程序能够适应链接故障.

延迟加载DLL

delayed指令添加到函数导入中.文件说:

要将包含该函数的库的加载推迟到实际需要该函数的时刻,请将延迟的指令附加到导入的函数:

function ExternalMethod(const SomeString: PChar): Integer; stdcall; 
  external 'cstyle.dll' delayed;
Run Code Online (Sandbox Code Playgroud)

delay确保在应用程序启动时不加载包含导入函数的库,而是在第一次调用函数时加载.

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

显式加载和绑定到DLL

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

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

在每个步骤中,必须在出现错误时检查函数返回值.如何处理错误,请记录MSDN文档中的每个Win32 API函数(如上所述).例如,如果找不到DLL,则LoadLibrary返回0.您必须检测到并相应地处理后果.

讨论

虽然该delayed指令非常方便,但我个人从未使用它.根据我的经验,每当我需要明确链接时,我总会发现我需要一些额外的灵活性delayed.也许我的需求是特殊的,但是如果你发现自己倾向于明确地打电话给LoadLibrary和,那就不要感到惊讶GetProcAddress.

举个例子,就在今天我发现自己正在使用,LoadLibraryEx因为我想传递LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR旗帜.使用时无法使用那种细粒度控件delayed.


Rob*_*edy 5

当函数以这种方式声明时,您的程序对丢失的 DLL 无法执行任何操作。操作系统会在程序代码的任何部分开始执行之前尝试解析导入的 DLL 函数,因此您无法编写对此执行任何操作的代码。

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

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

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

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

这两个选项将在另一个问题中进行更详细的讨论,该问题集中于仅在需要时使用 DLL

  • “延迟”功能只是“LoadLibrary()”和“GetProcAddress()”函数的编译器/RTL 包装器。使用“delayed”的另一个好处是,有“SetDliNotifyHook2()”和“SetDliFailureHook2()”函数允许您分配挂钩,以便您可以分别处理运行时加载通知和失败。因此,如果在运行时找不到给定的 DLL,甚至给定的函数,您可以记录错误,甚至用另一个 DLL 句柄或函数指针替换它来满足负载。 (6认同)