Delphi中调用C约定不定参数函数的调用方法.

    技术2022-05-11  55

    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)