Originally Posted by
masterBB
But doesn't the stack needs to be cleared or something? Or does the call do that.
Also is the result always in eax or it just a pascal thingy? It stores eax in the pascal Result making the assembly code return something.
I would apreciate if you can answer those two questions about call
thanks in advance for reading this.
Stdcall requires no cleanup. Cdecl requires manually cleanup. In assembly it's usually done by doing "ret (ArgCount * 4)". In the below, you'll notice I used a loop instead and popped each one off manually. That's because I'm not sure if Pascal's assembly will "ret" or not. Better to be safe than sorry.
The result is ALWAYS in eax for 32-bit assembly and rax for 64-bit assembly. It is never stored anywhere else. If your 32-bit code wants to return a 64-bit value, then the result is stored in a pair of registers: eax and edx. To turn them into a 64-bit value in 32-bit code, you'd just do:
mov lowBits, eax
mov highBits, edx
Result := (lowBits shl 32) or highBits.
Code explanation:
Simba Code:
asm
mov ecx, ArgC //Move the argument count into a register.
mov edx, Arg //Move the address of arguments[0] into a register.
@@start: //while (ArgCount > 0)
dec ecx //We decrease the arg count and use it as an index in the next line..
push dword ptr[edx + ecx * 4] //Push Argument[--ArgCount].
jnz @@start //If the argument count is 0, we break out of the loop and call the function. We've pushed enough arguments already.
call [Address] //call the function with all the arguments pushed onto the stack in reverse order (CDECL)
mov ecx, ArgC //move the original argument count back into a register.
@@end: //while (ArgCount > 0)
dec ecx
pop edx //Cleanup the stack by deleting: Pop Argument[--ArgCount].
jnz @@end //Are we finished with ALL the arguments?
mov @Result, eax //If so, move the result of the function call into "Result"
end
This is essentially the same as:
Simba Code:
Function callFunc(Addr: PtrUInt; ArgCount: Integer; Arguments: Array of AnyArgs): Pointer;
begin
For I := ArgCount - 1 DownTo 0 Do
begin
push(Arguments[I]);
end;
Result := Call(Address);
end;
Where AnyArgs is array of PtrUInt.
This is the most generic func I could come up with so far, to handle both stdcall and cdecl and functions with no args:
Simba Code:
Function CallFunc(Address: PtrUInt; ArgC: UInt32; Arg: Array of PtrUInt; isCDecl: Boolean = True): PtrUInt;
{$ASMMODE INTEL}
begin
if (isCDecl and (ArgC > 0)) then
asm
mov ecx, ArgC
mov edx, Arg
@@start:
dec ecx
push dword ptr[edx + ecx * 4]
jnz @@start
call [Address]
mov ecx, ArgC
@@end:
dec ecx
pop edx
jnz @@end
mov @Result, eax
end else
if (ArgC > 0) then
asm
mov eax, 0
mov ecx, ArgC
mov edx, Arg
@@start:
push dword ptr[edx + eax * 4]
cmp eax, ecx
inc eax
jl @@start
call [Address]
mov @Result, eax
end else
asm
call [Address]
mov @Result, eax
end;
end;
begin
CallFunc(PtrUInt(@Foo), 3, [PtrUInt(Arg1), PtrUInt(Arg2), PtrUInt(Arg3)]); //Calling CDECL function.
CallFunc(PtrUInt(@Bar), 3, [PtrUInt(Arg1), PtrUInt(Arg2), PtrUInt(Arg3)], false); //Calling STDCALL function.
CallFunc(PtrUInt(@Meh), 0, []); //calling either CDECL OR STDCALL with no args.
end.
This is what VariantInvoke should have really been.