通过Delphi访问Windows API会有性能损失吗?

mag*_*nus 8 c++ delphi winapi

我有几年前用C++中的旧Win32 API编程的经验,最近我参与了Delphi的开发.我马上就认出了很多从Windows API函数(如CreateThread,CreateWindowEx等,等).

我发现Embarcadero的文档不完整(至少可以说),通常可以参考Microsoft网站获取文档.在哪里,我可以补充一下,所有的功能都是用C语言定义的,这使得那里的非C人很难(但对我来说更容易).

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

Ken*_*ite 20

阅读来源.调用CreateWindowExWindows.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';
Run Code Online (Sandbox Code Playgroud)

所以你的具体问题的答案是否定的.没有性能损失.在Delphi中调用WinAPI函数不会导致性能损失.

  • @DavidHeffernan:你和肯已经浪费了3打评论**挑剔**实施细节**与提出的问题或提供的答案无关**.事实仍然是,Delphi**静态链接**到大多数API函数(一些API使用延迟加载,一些使用包装器来解释操作系统版本差异),因此WHO关注是否静态调用API函数是**直接**打电话或**IAT跳**电话.调用静态API函数,它最终以任何方式调用系统DLL.请将此讨论暂时搁置并继续. (5认同)
  • @DavidHeffernan Delphi摇滚太多,甲骨文被扔石头...... http://www.biblicalarchaeology.org/daily/ancient-cultures/daily-life-and-practice/the-oracle-of-delphi%E2%80% 94was-她,真的,砸死/ (4认同)
  • 你是对的.看起来签名只是为DLL提供符号解析:`function CreateThread; 外部kernel32名称'CreateThread';`很高兴知道. (3认同)
  • @DavidHeffernan这个答案是对的.Delphi设计与Windows下的本机应用程序一样高效. (3认同)
  • @arnaud不,不是.再次阅读答案.它声称对使用external声明的函数的调用是"直接"调用.显然不是.你认为呼叫+ jmp是直接的吗? (2认同)
  • @David:你会更容易停止这么烦人.我已证明(有证据)我已经正确回答了这里提出的问题,你对"番茄"或"tomahto"的争论是无关紧要的,因为你迫切需要让某人同意你是对的.我不是在浪费时间和你争论.如果你想继续评论,请随意这样做.正如我之前至少说过两次,如果你想对所提出的问题进行正确回答,请这样做.如果我的回答是错误的,请按照我的要求发布证明它的基准. (2认同)
  • @David:实际上,你错了,基于你自己的答案:"然后,Windows.CloseHandle实现如下:`JMP kernel32.CloseHandle`." 我的回答:Windows.pas中的"CreateWindowEx"直接调用User32.DLL中的`CreateWindowExW`函数.根据你自己的回答,我的陈述是正确的.你说从Windows.pas到`kernel32`的JMP,我说从Windows.pas到`user32`的电话.他们说同样的话. (2认同)

Dav*_*nan 8

调用Delphi Windows API函数是否立即调用Windows API函数?

不是没有.

为了争论,让我们考虑一下CloseHandle.这在Windows单元中声明并使用实现external.当你调用它,你实际上调用名为功能CloseHandleWindows单元.所以在伪汇编程序中它看起来像这样:

.... prepare parameters
CALL     Windows.CloseHandle
Run Code Online (Sandbox Code Playgroud)

然后,Windows.CloseHandle实现如下:

JMP      kernel32.CloseHandle
Run Code Online (Sandbox Code Playgroud)

因此,与直接调用相比,调用thunk函数,然后跳转到Win32 DLL.这被称为蹦床.

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

CALL     DWORD PTR [__imp__CloseHandle@4]
Run Code Online (Sandbox Code Playgroud)

这里,__imp__CloseHandle@4是内存中包含CloseHandleWindows DLL中地址的位置的地址.装载机写的实际地址CloseHandle__imp__CloseHandle@4在加载时间.

哪个更有效率?没有剖析,不可能肯定地说.但我相信,在极少数情况下,任何差异都会很大.

当然,也可以生成直接调用而没有间接调用的代码.这将涉及加载器补丁每次调用该函数.这可能是一个坏主意,但是因为它会导致大量的加载时间修复,这将是启动时的性能问题.也就是说,它与需要在加载时重新定位的DLL几乎相同.无论如何,我知道没有采用这一政策的工具链.

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

正如许多人所说,Win32函数被调用而没有中间层.因此,在这些意义上直接调用它们将您的参数未经修改地传递给API函数.但是呼叫机制是间接的,因为它使用蹦床.语义上没有区别.

  • 这是[库链接确实发生]的实现细节(http://msdn.microsoft.com/en-us/library/ms681914).这就是Windows加载其.dll的方式:在加载时,它解析符号名称并填充JMP表.然后代码中的单个调用引用此JMP表.这允许仅在内存中映射.dll的JMP表部分,而不需要映射整个.dll代码,该代码保持不变.因此,**Delphi设计与本机应用程序一样高效**. (6认同)
  • @David我在之前的评论中已经回答了这些问题.但是,你的断言"它可以让我更有效率"可以解释,因为"高效"可能不仅仅是对API的简单调用.在我看来,Arnaud正确地指出Delphi方式是最有效的方式(它还将重定位表的大小减少到每个API函数调用一个,而不是每个调用一个),因此(IMO)你的陈述"它可能更有效"是不正确的. (4认同)
  • 作为使用嵌入式系统的人,负载可能每月发生一次,而"现代CPU"很可能是基于586的(即Quark),我非常倾向于同意David的观点."高效"的定义取决于上下文,如果我最关心执行速度,那么现在进行CALL而不是JMP + CALL对我来说比上周加载应用程序需要多长时间更重要.OP问题专门询问了呼叫性能.大卫解决了这个问题. (2认同)
  • @DavidHeffernan`CALL DWORD PTR [__imp__CloseHandle @ 4]`是一个*间接调用*,就像`CALL Windows.CloseHandle; JMP kernel32.CloseHandle`.第二个可能会更快,因为它不会读取任何内存.唯一的直接调用可能是`CALL kernel32.CloseHandle` - 它可能不比其他两个快. (2认同)