Reputation: 7340
I am converting the ICallFrame interface to Delphi and I am not sure how to handle the ICallFrame::Invoke method.
This is the definition:
HRESULT Invoke(
void *pvReceiver,
...
);
Per documentation the varargs directive works only with external routines and only with the cdecl calling convention.
It's a bit strange as Invoke is declared with STDMETHODCALLTYPE
thus __stdcall
, which is a callee-clean convention. But a variadic function cannot be callee-clean since the callee does not know how many parameters were passed.
Is there some kind of compiler magic if you do this with Visual Studio?
Even if compiler magic uses cdecl, the Delphi compiler doesn't accept this (because it's not external) E2070 Unknown directive: 'varargs'
ICallFrame = interface(IUnknown)
['{D573B4B0-894E-11d2-B8B6-00C04FB9618A}']
// other methods left out
function Invoke(pvReceiver: PVOID): HRESULT; cdecl; varargs;
end;
Based on Rudy's answer it seems this could work:
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);
I will test with this and report back... EDIT: tested and works.
Upvotes: 3
Views: 678
Reputation: 28836
Stdcall
cannot use variable arguments. Any C or C++ function taking variable arguments must be __cdecl
, because only in that calling convention, the caller (code), which knows how many parameters were passed, cleans up the stack. Even if the default calling convention is stdcall
, var args functions will be cdecl
.
Delphi can not generate functions like that, but it can consume existing external C functions (in a DLL or .obj file) using the varargs
directive.
So a C (or C++) function like
HRESULT Invoke(void *pvReceiver, ...);
should be converted as:
function Invoke(pvReceiver: Pointer); cdecl; varargs;
Note that you leave out the ...
part in the Delphi declaration. It is assumed when you use the varargs
directive.
And that allows you to use it in Delphi like in C or C++:
Invoke(someReceiver, 17, 1.456);
Another example: for instance the well known C function printf()
:
int printf(const char *format, ...);
is declared as
function printf(format: PAnsiChar {args}): Integer; cdecl; varargs;
in System.Win.Crtl.pas.
This seems to be a method of an interface.
Not sure if that works, but I would try:
type
TInvoke = function(Intf: IInterface; pvReceiver: Pointer): HRESULT; cdecl varargs;
// No semicolon between cdecl and varargs.
and use it as:
MyResult := TInvoke(@myIntf.Invoke)(myIntf, someReceiver, 11, 17.3);
This method may have been declared as __stdcall
(through the STDMETHODCALLTYPE
macro), but even then, if it is variadic, the (VC++) compiler will generate __cdecl
, as Remy Lebeau found in Raymond Chen's blog.
Upvotes: 4