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

Convert variable arguments in C stdcall interface method to Delphi

我正在转换 ICallFrame interface to Delphi and I am not sure how to handle the ICallFrame::Invoke 方法。

这是定义:

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

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

这有点奇怪,因为 Invoke 是用 STDMETHODCALLTYPE 声明的,因此 __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 无法 生成 那样的函数,但它可以 使用现有的外部 C 函数(在 DLL 或.obj 文件)使用 varargs 指令。

所以像这样的 C(或 C++)函数

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

应转换为:

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

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

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

Invoke(someReceiver, 17, 1.456);

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

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

声明为

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

System.Win.Crtl.pas.

更新

这好像是一个接口的方法。

不确定是否可行,但我会尝试:

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