在Delphi中调用C约定不定参数函数的步骤
在Delphi中调用C约定不定参数函数的步骤
Delphi支持C约定的调用Cdecl大家都是知道的.最近一个朋友说C中不确定参数的函数Delphi中无法调用.其实Delphi支持内嵌汇编,这样任何函数约定都可以调用的.
我做了一个例子,选取的函数是NtDll中的DbgPrint函数.这个函数在C中的原型
ULONG
DbgPrint(
IN PCHAR Format,
. . . . [arguments]
);
是典型的C约定,不确定参数的函数.
Delphi中确实没有可以直接调用的约定方式.但是Delphi本身也支持一种不确定参数的调用方式就是array of const参数.只要稍加变通就行了.
先要声明DbgPrint函数.因为我们是内嵌汇编方式调用的,参数是汇编压栈,恢复现场的.所以参数部分无需声明出来,约定也无所谓.我这里就先暂定给Stdcall约定了.
代码我也不想在解释了,就加了详细的注释
function __DbgPrint : Integer; stdcall; external 'ntdll.dll' name 'DbgPrint';
function DbgPrint(Format : PChar; Args : array of const) : Cardinal; stdcall;
type
//这个类型的定义纯属是为了取Int64的高位和低位方便的.
LARGE_INTEGER = record
case Integer of
0 : (
LowPart : DWORD;
HighPart : Longint);
1 : (
QuadPart : LONGLONG);
end;
PLARGE_INTEGER = ^LARGE_INTEGER;
var
LI, LJ : Integer;
LArgs : array[0..63] of Cardinal;
LInt : LARGE_INTEGER;
I : Integer;
begin
Result := 0;
LJ := 0;
//把参数整理到本地数组LArgs中
for LI := 0 to High(Args) do
begin
with Args[LI] do
begin
if VType = vtInteger then //整数
LArgs[LJ] := VInteger
else
if VType = vtBoolean then //Boolean
LArgs[LJ] := Cardinal(VBoolean)
else
if VType = vtChar then //Char
LArgs[LJ] := Cardinal(VChar)
else
if VType = vtPChar then //PChar类型
LArgs[LJ] := Cardinal(VPChar)
else
if VType = vtPointer then //指针
LArgs[LJ] := Cardinal(VPointer)
else
if VType = vtAnsiString then //字符串
LArgs[LJ] := Cardinal(VAnsiString)
else
if VType = vtInt64 then //Int64,Int64是拆分成高位和低位,送进参数的
begin
LInt.QuadPart := VInt64^;
LArgs[LJ] := Cardinal(LInt.LowPart);
Inc(LJ);
LArgs[LJ] := Cardinal(LInt.HighPart);
end
else
LArgs[LJ] := $DEADBEEF;
end;
Inc(LJ);
end;
asm
lea eax,LArgs //把参数数组地址装入Eax
mov ecx,LJ //把参数个数装入Ecx
test ecx,ecx //检测参数个数是不是0
jl @Exit_Loop
@args_loop: //循环把参数压入栈中
mov edx,ecx
dec edx
push dword ptr [eax+4*edx]
@cmp_args_end:
dec ecx
jnz @args_loop
@Exit_Loop: //循环结束点
push Format //压入格式字符串
call __DbgPrint //调用__DbgPring
//C调用约定要由调用方回复堆栈平衡
mov ecx,LJ //参数个数给Ecx
shl ecx,002h //参数都是4字节,所以把Ecx乘4
add ecx,004h //再加上Format指针的4个字节
add esp,ecx //栈顶指针加ECX,也就是回复栈
mov Result,eax //结果
end;
end;
调用方式:
var
I : Int64;
begin
I:=900000000;
DbgPrint('你的名字是:%s;数字:%d;再来一个Int64的:%I64d', ['王锐',1,I]);
end;
执行之后就可以在Delphi的Event Log窗口看到输出调试的信息了.