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窗口看到输出调试的信息了.