我有几年前用C ++中的旧Win32 API编程的经验,最近我参与了Delphi的开发。我立即认识到Windows API中的许多功能(例如,CreateThread
,CreateWindowEx
等)。
我发现Embarcadero的文档不完整(至少可以说),通常可以参考Microsoft网站获取文档。在哪里,我可以补充一下,所有的功能都是用C语言定义的,这使得那里的非C人很难(但对我来说更容易)。
我想知道的是 - 鉴于Delphi函数签名与Microsoft提供的C函数签名相同 - 调用Delphi Windows API函数会立即调用Windows API函数,还是调用相同的Delphi函数然后调用它调用Windows API函数并返回结果,前者意味着与相应的C代码相比没有可区别的性能差异,而后者意味着性能损失?
阅读来源。对CreateWindowEx
单元的调用在Windows.pas
单元中定义为直接调用CreateWindowExW
中的User32.DLL
函数(来自XE5的源代码 - 类似的定义可在所有版本的Delphi中找到支持的OS版本):
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
实施的。当你调用它时,你确实在CloseHandle
单元中调用了一个名为Windows
的函数。所以在伪汇编程序中它看起来像这样:
.... 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函数。但是呼叫机制是间接的,因为它使用蹦床。语义上没有区别。