《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声明形式!
本文转自 不得闲 博客园博客,原文链接:http://www.cnblogs.com/DxSoft/archive/2013/04/17/3025475.html ,如需转载请自行联系原作者