I am experimenting with the ability to dynamically invoke procedures or functions that reside in a function table. The specific application is a DLL that exports a pointer to a function table together with information on the number of arguments and types. The host application then has the ability to interrogate the DLL and call the functions. If they were object methods I could use Rtti to invoke them but they are normal procedures and functions. The DLL has to export normal function pointers not objects because the DLL could be written in any language including C, Delphi etc.
For example, I have a record declared and filled out in a DLL:
TAPI = record
add : function (var a, b : double) : double;
mult : function (var a, b : double) : double;
end;
PAPI = ^TAPI;
I retrieve the pointer to this record, declared as:
apiPtr : PAPI;
Assume I also have access to the names of the procedures, number of arguments and argument types for each entry in the record.
Assume I want to call the add function. The function pointer to add will be:
@apiPtr^.add // I assume this will give me a pointer to the add function
I assume there is no other way other than to use some asm to push the necessary arguments on the stack and retrieve the result?
First question, what is the best calling convention to declare the procedure as, cdecl? Seems easiest for assembling the stack before the call.
Second question, are there any examples online that actually do this? I came across http://www.swissdelphicenter.ch/torry/showcode.php?id=1745 (DynamicDllCall) which is close to what I want but I simplified as below, it now returns a pointer (EAX) to the result:
function DynamicDllCall(proc : pointer; const Parameters: array of Pointer): pointer;
var x, n: Integer;
p: Pointer;
begin
n := High(Parameters);
if n > -1 then begin
x := n;
repeat
p := Parameters[x];
asm
PUSH p
end;
Dec(x);
until x = -1;
end;
asm
CALL proc
MOV p, EAX <- must be changed to "FST result" if return value is double
end;
result := p;
end;
but I can't get it to work, it returns a value for the first parameters instead of the result. Maybe I have the calling convention wrong or maybe I misunderstand how to retrieve the result in EAX.
I call DynamicDllCall as follows:
var proc : pointer;
parameters: array of Pointer;
x, y, z : double;
p : pointer;
begin
x:= 2.3; y := 6.7;
SetLength(parameters, 2);
parameters[0] := @x; parameters[1] := @y;
proc := @apiPtr^.add;
p := DynamicDllCall(proc, Parameters);
z := double (p^);
Any advice gratefully received. I appreciate that some may feel this isn't the way one should go about doing this but I am still curious if it is at least possible.
Update 1 I can confirm that the add function is getting the correct values to do the addition.
Update 2 If I change the signature of add to:
add : function (var a, b, c : double) : double;
and I assign the result to c inside add, then I can retrieve the correct answer in the parameters array (assuming I add one more element to it, 3 instead of 2). The problem therefore is that I misunderstand how values are returned from functions. Can anyone explain how functions return values and how best to retrieve them?
Update 3 I have my answer. I should have guessed. Delphi returns different types via different registers. eg integers return via EAX, double on the other hand returns via ST(0). To copy ST(0) to the result variable I must use "FST result" rather than "MOV p, EAX". I least I now know it is possible in principle to do this. Whether it is a sensible thing to do is another matter I must now think about.
Function Results
topic. – Ken White