通过Delphi访问Windows API是否会导致性能损失



几年前,我在C++中使用旧的Win32 API进行编程,最近我参与了Delphi的开发。我立即从Windows API中识别出许多函数(例如CreateThreadCreateWindowEx等)。

我发现Embarcadero的文档不完整(至少可以说),通常会参考微软网站上的文档。我可以补充一点,所有的函数都是用C定义的,这让非C的人很难理解(但对我来说更容易)。

我想知道的是-假设Delphi函数签名与Microsoft提供的C函数签名相同-立即调用Delphi Windows API函数调用Windows API函数,还是调用相同的Delphi函数,然后调用Windows API函数并返回结果,前者意味着与相应的C代码相比,性能没有明显差异,而后者则意味着性能损失?

读取源代码。对CreateWindowEx的调用在Windows.pas单元中定义为对User32.DLLCreateWindowExW函数的直接调用(来自XE5的源代码-对于支持的操作系统版本,在Delphi的所有版本中都有类似的定义):

function CreateWindowEx(dwExStyle: DWORD; lpClassName: LPCWSTR;
lpWindowName: LPCWSTR; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND;
stdcall; external user32 name 'CreateWindowExW';

因此,你的具体问题的答案是否定的。没有绩效惩罚。在Delphi中调用WinAPI函数不会对性能造成影响。

对Delphi Windows API函数的调用会立即调用Windows API函数吗?

不是。

为了便于讨论,让我们考虑对CloseHandle的调用。这是在Windows单元中声明的,并使用external来实现。当您调用它时,实际上是在Windows单元中调用了一个名为CloseHandle的函数。所以在伪汇编程序中,它看起来是这样的:

.... prepare parameters
CALL     Windows.CloseHandle

然后,Windows.CloseHandle是这样实现的:

JMP      kernel32.CloseHandle

因此,与直接调用相比,有一个对thunk函数的调用,然后跳转到Win32 DLL。这被称为蹦床。

它可以以不同的方式实施。编译器可以发出代码直接调用Win32 DLL。有些编译器会这样做。例如,MSVC发出的此调用的等效asm为:

CALL     DWORD PTR [__imp__CloseHandle@4]

这里,__imp__CloseHandle@4是存储器中包含Windows DLL中CloseHandle的地址的位置的地址。加载程序在加载时将CloseHandle的实际地址写入__imp__CloseHandle@4

哪个更有效?如果没有分析,就无法确定。但我相信,在极少数情况下,任何差异都将是显著的。

当然,也有可能生成直接调用而不间接调用的代码。这将涉及加载程序对函数的每次调用进行修补。然而,这可能是一个坏主意,因为它会导致大量的加载时修复,这在启动时会是一个性能问题。也就是说,它与加载时需要重新定位的DLL基本相同。无论如何,据我所知,没有任何工具链采用这一政策。

也许您关心的是这些函数是否是真正的Win32函数。或者它们周围是否有一层改变了意义。这些是真正的Win32函数。没有Delphi文档,因为这些都是Win32函数。MSDN上的Win32文档是文档的权威来源。

正如许多人所说,Win32函数的调用没有中间层。因此,在这种意义上,它们是直接调用的,即您的参数在未修改的情况下传递给API函数。但调用机制是间接的,因为它使用蹦床。从语义上讲没有区别。

最新更新