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

后来知道cdecl调用约定加一个varargs修饰符即可.

此条目发表在未分类分类目录。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

你必须启用JavaScript