将C stdcall接口方法中的变量参数转换为Delphi

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将C stdcall接口方法中的变量参数转换为Delphi相关的知识,希望对你有一定的参考价值。

我正在将ICallFrame接口转换为Delphi,我不知道如何处理ICallFrame::Invoke方法。

这是定义:

HRESULT Invoke(
  void *pvReceiver,
  ...  
);

根据文档,varargs指令仅适用于外部例程,并且仅适用于cdecl调用约定。

这有点奇怪,因为用STDMETHODCALLTYPE声明了Invoke,因此__stdcall,这是一个被调用的清洁惯例。但是,由于被调用者不知道传递了多少个参数,因此可变参数函数不能被调用。如果使用Visual Studio执行此操作,是否存在某种编译器魔法?

即使编译器魔术使用cdecl,Delphi编译器也不接受这个(因为它不是外部的)E2070 Unknown directive: 'varargs'

ICallFrame = interface(IUnknown)
  ['{D573B4B0-894E-11d2-B8B6-00C04FB9618A}']
  // other methods left out
  function Invoke(pvReceiver: PVOID): HRESULT; cdecl; varargs;
end;

基于Rudy的答案,似乎这可行:

type
    TfnInvoke = function(this: Pointer; pvReceiver: Pointer): HRESULT; cdecl varargs;

implementation 

function GetInterfaceMethod(const intf; methodIndex: dword) : pointer;
begin
  Result := Pointer(pointer(dword_ptr(pointer(intf)^) + methodIndex * SizeOf(Pointer))^);
end;
...
var
  Invoker: TfnInvoke;
  ...
  // Don't forget IUnknown methods when counting!
  Invoker := GetInterfaceMethod(myIntf, 21);
  Invoker(myIntf, FObject, 11, 17.3);

我将用此测试并报告回来...编辑:测试和工作。

答案

Stdcall不能使用变量参数。任何带有变量参数的C或C ++函数都必须是__cdecl,因为只有在调用约定时,调用者(代码)知道传递了多少参数,才能清理堆栈。即使默认调用约定是stdcall,var args函数也将是cdecl

Delphi不能生成这样的函数,但它可以使用varargs指令消耗现有的外部C函数(在DLL或.obj文件中)。

所以C(或C ++)函数就好

HRESULT Invoke(void *pvReceiver, ...);

应转换为:

function Invoke(pvReceiver: Pointer); cdecl; varargs;

请注意,您在Delphi声明中省略了...部分。使用varargs指令时会假设它。

这允许你在Delphi中使用它,就像在C或C ++中一样:

Invoke(someReceiver, 17, 1.456);

另一个例子:例如众所周知的C函数printf()

int printf(const char *format, ...);

被宣布为

function printf(format: PAnsiChar {args}): Integer; cdecl; varargs;

在System.Win.Ctrl.pad中。

Update

这似乎是一种界面的方法。

不确定这是否有效,但我会尝试:

type
  TInvoke = function(Intf: IInterface; pvReceiver: Pointer): HRESULT; cdecl varargs; 
  // No semicolon between cdecl and varargs.

并将其用作:

MyResult := TInvoke(@myIntf.Invoke)(myIntf, someReceiver, 11, 17.3);

这个方法可能已被声明为__stdcall(通过STDMETHODCALLTYPE宏),但即使这样,如果它是可变参数,(VC ++)编译器将生成__cdecl,因为Remy Lebeau发现了in Raymond Chen's blog

以上是关于将C stdcall接口方法中的变量参数转换为Delphi的主要内容,如果未能解决你的问题,请参考以下文章

在 python 中的 byref 向量参数上使用 ctypes 进行 DLL 转换

_STDCALL&_CDECL 调用约定

__stdcall __cdecl的区别

qt能修改函数调用协议为stdcall吗

为 __stdcall 函数指针提供比预期更多的参数

C语言各个数据类型取值范围