zoukankan      html  css  js  c++  java
  • Delphi的参数传递约定以及动态参数个数(转载笔记)

       《Delphi中的参数传递约定概述》

    由于Delphi太好用了以至于大多数Delphi fans对Delphi约定都没什么认识...
    抱歉其实大部分人的确是这样的
    这里写下一篇浊文仅供大家参考-转载保留版权.谢谢大家支持

    1.register-Delphi默认模式
    参数传递模式...前三个数据.eax,edx,ecx...超过三个参数部分.放在堆栈传递
    其他的方法和...和stdcall一样...函数自己恢复堆栈

    按照这个传递模式,所以说..这样效果编译器会更容易优化一些?呵呵
    procedure XorMemory(lpMemory: Pointer; bKey: BYTE; dwLen: DWORD);
    begin
      while (dwLen > 0) do
      begin
        PBYTE(lpMemory)^ := PBYTE(lpMemory)^ xor bKey;
        Inc(PBYTE(lpMemory));
        Dec(dwLen);
      end;
    end;

    2.pascal-目前基本上不使用了

    3.cdecl-C语言调用约定(从右向左压栈.调用者恢复堆栈)
    这个模式在Delphi下是一个很争议的话题..
    怎么说呢..比如说wsprintf等函数都是错误的翻译...
    C\C++下是采用cdecl调用约定,而Delphi下全部翻译成stdcall模式...

    而且C\C++总是配合可变参数一起使用的...
    而Delphi下也是有可变参数标记的一般用户很少去关注如何使用罢了
    具体看一下windows.pas
    function wsprintf(Output: PChar; Format: PChar): Integer; stdcall;
    这里的声明类型完全是错误的...如果想要和C一样的方式

    function wsprintf(Output: PChar; Format: PChar): Integer; cdecl; varargs; external user32 name 'wsprintfA';
    
    function  DbgPrint(Format:PChar): NTSTATUS; cdecl; varargs; external NtKernel name 'DbgPrint';
    function  _snprintf(buffer: PChar; nsize: Integer; const fmt: PChar): Integer; cdecl; varargs; external NtKernel name '_snprintf';
    

      自己单独写一个函数声明...即可...你可以变参调用了...
    使用的时候...和C\C++下使用完全一样


    这里有一个窍门...这样的函数我们如何声明函数类型?
    找了大量的资料还是没招.不知道如何测试居然测试成功了...这叫啥?不知道

    program Project2;
    
    uses
      Windows;
    
    //  注意看下面--cdecl varargs;之间是没有;号的
    type
      TwsprintfA = function(Output: PAnsiChar; Format: PAnsiChar): Integer; cdecl varargs;
    
    var
      fnwsprintfA: TwsprintfA;
      szBuffer: Array[0..MAX_PATH] Of Char;
    begin
      @fnwsprintfA := GetProcAddress(LoadLibrary('user32'), 'wsprintfA');
      fnwsprintfA(szBuffer, 'Id: %s, Age: %d', 'Anskya', 18);
      MessageBox(0, szBuffer, 'By Anskya', 0);
    end.
    4.stdcall-标准调用约定(从右向左压栈.函数自己恢复堆栈)
    这个...基本上api都是采用如此调用模式..编写动态运行库的
    比较重要的约定

    5.safecall-Delphi不支持..唉~牧龙鼠大牛抱歉我解决半天也没搞定
    这个约定C\C++支持,其实和register约定出奇的相似.
    支持传递参数的寄存器不一样.  
     
     

    Delphi编写变长参数的cdecl函数

    学过C语言的人都知道,printf的参数是不固定的,这种参数形式叫做变长参数。带有变长参数的函数必须是cdecl调用约定,由函数的调用者来清除栈,参数入栈的顺序是从右到左。printf是根据格式化串中的占位标记来猜测栈中的参数的。以下就用Delphi模拟一个简单的类似printf的函数sprintf。

    type
       VA_FN = function(const par1, par2{, }: Pointer): Integer; cdecl varargs;
    
    procedure CvtInt;
    { IN:
         EAX:   The integer value to be converted to text
         ESI:   Ptr to the right-hand side of the output buffer:   LEA ESI, StrBuf[16]
         ECX:   Base for conversion: 0 for signed decimal, 10 or 16 for unsigned
         EDX:   Precision: zero padded minimum field width
       OUT:
         ESI:   Ptr to start of converted text (not start of buffer)
         ECX:   Length of converted text
    }
    asm
             OR       CL,CL
             JNZ      @CvtLoop
    @C1:     OR       EAX,EAX
             JNS      @C2
             NEG      EAX
             CALL     @C2
             MOV      AL,'-'
             INC      ECX
             DEC      ESI
             MOV      [ESI],AL
             RET
    @C2:     MOV      ECX,10
    
    @CvtLoop:
             PUSH     EDX
             PUSH     ESI
    @D1:     XOR      EDX,EDX
             DIV      ECX
             DEC      ESI
             ADD      DL,'0'
             CMP      DL,'0'+10
             JB       @D2
             ADD      DL,('A'-'0')-10
    @D2:     MOV      [ESI],DL
             OR       EAX,EAX
             JNE      @D1
             POP      ECX
             POP      EDX
             SUB      ECX,ESI
             SUB      EDX,ECX
             JBE      @D5
             ADD      ECX,EDX
             MOV      AL,'0'
             SUB      ESI,EDX
             JMP      @z
    @zloop: MOV      [ESI+EDX],AL
    @z:      DEC      EDX
             JNZ      @zloop
             MOV      [ESI],AL
    @D5:
    end;
    
    function sprintf(lpcszFormat:PAnsiChar;lpszBuf:PAnsiChar;BufLen:Integer{Arg1,Arg2}):Integer;cdecl;
      label Scan,ExitProc,MeetInteger,Movbuf,ScanLoop,CopyLoop,label1,label2,label3,Check1;
    asm
      {
       编译器自动加的指令:
       push ebp
       mov ebp,esp
      }
       add ebp,20 {ebp指向第一个格式参数}
       push edx
       push esi
       push edi
       sub esp,24   { 16字符的缓冲区+4字节保存当前格式化串的子串指针+4字节=24 }
       lea esi,[esp+24] {esi保存字符缓冲区结尾地址}
       mov edx,[ebp-8] { edx保存目标串当前位置 }
       mov ebx,[ebp-12]  { ebx保存格式化串当前位置 }
    Scan:
       mov eax,ebx
       cmp byte ptr [eax],0
       jz ExitProc
       push edx
       mov dx,'%'
       call StrScan (* 查找占位符% *)
       pop edx
       test eax,eax
       jz ExitProc
       mov ecx,eax
       sub ecx,ebx
       call label1
       jmp label2
    label1:
       push eax
       CopyLoop:
       mov al,[ebx]
       cmp al,0  {遇到0字符终止}
       jz label3
       mov [edx],al
       inc ebx
       inc edx
       loop CopyLoop
    label3:
       pop eax
       ret
    label2:
       cmp byte ptr [eax+$01],'d'
       jnz Check1
       add ebx,2
       call MeetInteger
       jmp Scan
    Check1:
       cmp byte ptr [eax+$01],'%'
       jnz ExitProc
       add ebx,2
       mov byte ptr [edx],'%'
       inc edx
       jmp Scan
    MeetInteger:
       push eax
       push esi
       push edx
       mov eax,[ebp]
       mov ecx,0  (* 有符号数 *)
       mov edx,0
       call CvtInt
       pop edx
       call MovBuf
       add ebp,4  {让ebp指向下一个参数}
       pop esi
       pop eax
       ret
    Movbuf:
       mov al,[esi]
       mov [edx],al
       inc esi
       inc edx
       loop MovBuf
       ret
    ExitProc:
       mov ecx,$7FFFFFFF
       call label1
       mov [edx],0
       mov Result,0
       add esp,24
       pop edi
       pop esi
       pop edx
      {
       编译器自动加的指令:
       pop ebp
       ret
      }
    end;
    

      另外,关于动态参数传递,在P4D早期版本中看到一种实现,就是将动态参数传递的动态参数映射成array of Pointer之类的数组来实现,具体实现方式如

    DLL_PyArg_Parse:   function( args: PPyObject; format: PChar {;....}) :Integer; cdecl;
    DLL_PyArg_ParseTuple:  function( args: PPyObject; format: PChar {;...}):Integer; cdecl;
    DLL_Py_BuildValue:  function( format: PChar {;...}): PPyObject; cdecl;

    先声明这些动态参数函数,然后实现

    {DELPHI does the right thing here. It automatically generates
    a copy of the argp on the stack}
    function TPythonInterface.PyArg_Parse ( args: PPyObject; format: PChar;
    argp: array of Pointer): Integer; cdecl;
    begin
    {$IFDEF DELPHI6_OR_HIGHER}
    Result := 0;
    { Do not optimize this to a "pure" assembler routine, because such
    a routine does not copy the array arguments in the prologue code }
    asm
    lea edx, format
    push [edx]
    
    sub edx, TYPE PChar
    push [edx]
    
    mov eax, Self
    mov eax, [eax].DLL_PyArg_Parse
    call eax
    
    pop edx
    pop edx
    mov Result, eax
    end;
    {$ELSE}
    Result := DLL_PyArg_Parse( args, format );
    {$ENDIF}
    end;
    
    function TPythonInterface.PyArg_ParseTuple ( args: PPyObject; format: PChar;
    argp: array of Pointer): Integer; cdecl;
    begin
    {$IFDEF DELPHI6_OR_HIGHER}
    Result := 0;
    { Do not optimize this to a "pure" assembler routine, because such
    a routine does not copy the array arguments in the prologue code }
    asm
    lea edx, format
    push [edx]
    
    sub edx, TYPE PChar
    push [edx]
    
    mov eax, Self
    mov eax, [eax].DLL_PyArg_ParseTuple
    call eax
    
    pop edx
    pop edx
    mov Result, eax
    end;
    {$ELSE}
    Result := DLL_PyArg_ParseTuple( args, format );
    {$ENDIF}
    end;
    

     早期P4D实现方式下载

    现在最新的P4D已经全部变成了Varargs声明形式!

    作者:不得闲
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原
    文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    提高SQL查询效率
    数据库主键设计之思考
    Hlg 1030 排序
    Hdu 1556 成段更新.cpp
    Hdu 4280 最大流<模板>.cpp
    POJ 3216 最短路径匹配+floyd
    Hdu 4268 multiset函数的应用
    ZOJ 3602 树的同构
    Hdu 4284 状态DP 能否走完所选城市.cpp
    Hlg 1481 二分图匹配+二分.cpp
  • 原文地址:https://www.cnblogs.com/DxSoft/p/3025475.html
Copyright © 2011-2022 走看看