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声明形式!



本文转自 不得闲 博客园博客,原文链接:http://www.cnblogs.com/DxSoft/archive/2013/04/17/3025475.html   ,如需转载请自行联系原作者


相关文章
|
7月前
|
SQL 负载均衡 数据可视化
第六章:参数和变量
第六章:参数和变量
440 1
|
1月前
|
C++
在C++语言中参数的传递
在C++语言中参数的传递
6 0
|
9月前
|
API
讨论问题--数据类型、数组、传值/址API函数等
讨论问题--数据类型、数组、传值/址API函数等
45 0
学习TypeScrip5(函数扩展)
学习TypeScrip5(函数扩展)
70 0
|
安全 JavaScript
学习TypeScrip2(任意类型)
TypeScript 3.0中引入的 unknown 类型也被认为是 top type ,但它更安全。与 any 一样,所有类型都可以分配给unknown
58 0
|
Dart 前端开发 JavaScript
dart中将方法当做参数传递时的注意事项
众所周知,Dart是一门面向对象的语言,比Java更纯粹,Dart中的方法也是对象,也有类型Function。这意味着方法可以被分配给对象,也可以当做参数传递给其他方法。
typescript14-(单独指定参数和返回值的类型)
typescript14-(单独指定参数和返回值的类型)
63 0
typescript14-(单独指定参数和返回值的类型)
|
缓存 Windows
Windows程序设计——WNDCLASS结构参数及其用法
Windows程序设计——WNDCLASS结构参数及其用法
295 0
|
缓存 前端开发 Swift
Swift实用小册06:函数的定义、参数、返回、调用
Swift实用小册06:函数的定义、参数、返回、调用
201 0
Swift实用小册06:函数的定义、参数、返回、调用
|
JavaScript 前端开发 开发者
函数当作参数2 | 学习笔记
快速学习函数当作参数2。