《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.
这个...基本上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已经全部变成了Varargs声明形式!
文连接,否则保留追究法律责任的权利。