博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Delphi的参数传递约定以及动态参数个数(转载笔记)
阅读量:6084 次
发布时间:2019-06-20

本文共 6170 字,大约阅读时间需要 20 分钟。

   《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 label2label1:   push eax   CopyLoop:   mov al,[ebx]   cmp al,0  {遇到0字符终止}   jz label3   mov [edx],al   inc ebx   inc edx   loop CopyLooplabel3:   pop eax   retlabel2:   cmp byte ptr [eax+$01],'d'   jnz Check1   add ebx,2   call MeetInteger   jmp ScanCheck1:   cmp byte ptr [eax+$01],'%'   jnz ExitProc   add ebx,2   mov byte ptr [edx],'%'   inc edx   jmp ScanMeetInteger:   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   retMovbuf:   mov al,[esi]   mov [edx],al   inc esi   inc edx   loop MovBuf   retExitProc:   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 generatesa 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 sucha routine does not copy the array arguments in the prologue code }asmlea edx, formatpush [edx]sub edx, TYPE PCharpush [edx]mov eax, Selfmov eax, [eax].DLL_PyArg_Parsecall eaxpop edxpop edxmov Result, eaxend;{$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 sucha routine does not copy the array arguments in the prologue code }asmlea edx, formatpush [edx]sub edx, TYPE PCharpush [edx]mov eax, Selfmov eax, [eax].DLL_PyArg_ParseTuplecall eaxpop edxpop edxmov Result, eaxend;{$ELSE}Result := DLL_PyArg_ParseTuple( args, format );{$ENDIF}end;

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

你可能感兴趣的文章
我的友情链接
查看>>
perl文件读写
查看>>
R-FCN
查看>>
DenseNet
查看>>
jspsmart 支持jdk1.4 解决utf-8编码时出现乱码的问题 附源码和jar包
查看>>
我的友情链接
查看>>
把LYNC从评估版升级到正式版
查看>>
我的友情链接
查看>>
eclipse 不能建立maven项目
查看>>
Session死亡讲解
查看>>
八周三次课(1月31日)
查看>>
我的友情链接
查看>>
关于linux中 变量相关 学习小白总结
查看>>
文德数据启动国庆中秋大优惠——现在购买立省三千
查看>>
每天一个python 小案例——循环和列表
查看>>
结构体/struct
查看>>
用VC++开发Oracle数据库应用程序详解
查看>>
CCS初学那点事(二)
查看>>
机器学习:数据预处理之独热编码(One-Hot)
查看>>
我的友情链接
查看>>