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

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值