【问题标题】:Can Delphi only use a .dll if required?如果需要,Delphi 可以只使用 .dll 吗?
【发布时间】:2012-02-13 16:03:24
【问题描述】:

我已将这两种方法添加到我的 Delphi 5 应用程序的第一个单元中。

function Inp(PortAddress: Integer): Integer; stdcall; external 'inpout32.dll' name 'Inp32';

procedure Output(PortAddress, Value: Integer); stdcall; external 'inpout32.dll' name 'Out32';

但是,除非他们明确需要,否则我不想在软件中发布 inpout32 库。目前程序在执行时会显示“未找到”,除非它们存在于根目录或 System32 中。

用户只有在设置了特定选项时才会调用这些方法,但在使用 inpout 库之前不会从 .ini 文件中收集这些方法。

有没有办法像某些组件一样只在需要时使用这个库,而不是像我一样声明它?

【问题讨论】:

  • 是的,您可以进行“动态加载”。使用LoadLibraryGetProcAddress,如here 所述。

标签: delphi dll delphi-5


【解决方案1】:

在 2010 之前的 Delphi 版本中,您必须使用经典的动态加载。考虑这个从Kernel32.dll 调用Beep 函数的典型(简单)示例(当然,您应该硬编码实际代码中的路径!):

type
  TBeepFunc = function(dwFreq: DWORD; dwDuration: DWORD): BOOL; stdcall;

procedure TForm4.FormClick(Sender: TObject);
var
  lib: HMODULE;
  prc: TBeepFunc;
begin

  lib := LoadLibrary('C:\WINDOWS\System32\Kernel32.dll');
  if lib = 0 then RaiseLastOSError;
  try
    @prc := GetProcAddress(lib, 'Beep');
    if Assigned(prc) then
      prc(400, 2000)
    else
      ShowMessage('WTF? No Beep in Kernel32.dll?!');
  finally
    FreeLibrary(lib);
  end;
end;

【讨论】:

  • 感谢@Andreas,这看起来是最简单的解决方案。不过,我的 Delphi5 中无法识别 RaiseLastOSError。我可以安全地删除 If 语句,因为错误总是会被第二条消息“WTF?没有进程或库没有加载?!”
  • @notidaho: 不,如果lib = 0 你不能进入try 块。您可以将RaiseLastOSError 替换为任何引发异常的代码,例如raise Exception.Create('Couldn't load library.')。当然,你也可以将questoin中的行替换为if lib <> 0 then,这样整个try..finally..end只有在lib有效的情况下才会运行。但最好显示一条错误消息。
  • RaiseLastOSError = RaiseLastWin32Error 在 D5 中
  • 这种方法非常有效,谢谢。我发出了一条用户友好的消息,因为我不确定最后一个引发的错误有多大用处?作为一个兴趣点,即使我只是尝试从根目录加载Library('input.dll'),如果库位于 System32 中,它仍然可以工作。
【解决方案2】:

此工具称为delay loading,是在 Delphi 2010 中添加的。

以您的代码为例,您可以这样编写导入:

function Inp(PortAddress: Integer): Integer; stdcall; 
    external 'inpout32.dll' name 'Inp32' delayed;

仅在第一次调用该函数时才会执行与此外部函数的绑定。如果绑定失败,则在运行时引发异常。

如果您需要更细粒度的控制,您可以使用SetDliNotifyHookSetDliFailureHook 自定义延迟加载行为。

一些补充产品文档的博客文章:


在旧版本的 Delphi 上,您可以使用 LoadLibraryGetProcAddress。或者,如果你想要一些更漂亮的东西,我可以衷心推荐 Hallvard Vassbotn 的延迟负载类,他描述了 in this blog article。这段代码包含了调用LoadLibraryGetProcAddress 的所有样板代码,使用起来只比新的Delphi 2010 内置功能稍微麻烦一些。

多年来,我成功地使用了 Hallvard 的图书馆。需要注意的一点是它不是线程安全的,因此如果多个线程同时尝试绑定到一个函数,那么代码可能会失败。这很容易通过在 Hallvard 的代码中添加内部锁来解决。

【讨论】:

  • 哦,所以不需要GetProcAddress 和所有这些了?太糟糕了,这很有趣......
  • @Andreas 就我个人而言,我仍在使用 GetProcAddress 和我自己的类来处理延迟加载,但是不,他们在 D2010 中添加了这个新功能。我担心你来得太晚了。
  • 谢谢 David 似乎是个好主意,但有什么方法可以在 Delphi5 中做到这一点?
  • 呸,我刚刚在你的文字中看到了 D5。初读时错过了。在标签 FWIW 中更好。在 D5 中,我会引导您使用 Hallvard Vassbotn 的延迟加载库 - 请参阅我的更新答案。
  • 别担心,大卫也许它对其他人有用。就我个人而言,我认为我会走 LoadLibrary 和 GetProcAddress 路线。欢呼
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-06
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 2021-01-14
  • 1970-01-01
  • 2014-09-03
相关资源
最近更新 更多