我正在将ICallFrame接口转换为Delphi,但不确定如何处理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;
根据鲁迪的回答,看来这可能有效:
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);
我将对此进行测试并报告... 编辑:经过测试,可以工作。
答案 0 :(得分:5)
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
指令。
这使您可以像在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一样。勒博发现了in Raymond Chen's blog。