Delphi中参数的传递和函数值的返回

关于Delphi中参数的传递和函数值的返回

前言:

高手们应该早知道了,不屑于写出来而已。真正的高手一个比一个潜的深,只剩下偶这样的小菜写些菜文给更小的菜。高手看时还请捂好大牙,多多指点。

不知各位小菜同胞对破解DELPHI程序有什么看法,反正我的感觉就一个字:怪。各位最先遇到的问题恐怕都是:我下了GetDlgItemInt、GetDlgItemText、GetWindowText....怎么什么也断不下来,甚至连Hmemcpy都不起作用?呵呵,从这里就能看出宝蓝的那批人成心想跟M$对着干,非搞出些新鲜的东东不可。

这回我们就来看看DLEPHI中对函数(过程)参数的传递是如何进行的。

我们知道WinAPI采用的调用约定是StdCall,也就是调用一个函数Func(arg1,agr2,agr3,arg4),你需要push arg4,push arg3,push arg2,push arg1,call Func 。在VC++里也是这种形式,所以一个函数有几个参数,可以非常直观地看出来。可是在DELPHI中就很奇怪了,在一个CALL前面你可能一个PUSH也看不到,怎么回事呢?听我慢慢道来。

DELPHI中的调用约定有StdCall,Cdecl,Safecall,Pascal和Register等几种方式,而DELPHI的默认方式是Register(为什么不是Pascal?)Register方式就是尽可能地使用寄存器来传递参数,减少堆栈的操作来提高速度。具体情况是怎样呢,看个例子先:

在FORM上放一个BUTTON,双击写代码如下:

代码:
function add1(a:Integer):Integer;    //一个参数
begin
     add1:=a+a;
end;

function add2(a,b:Integer):Integer;    //两个参数
begin
     add2:=a+b;
end;

function add3(a,b,c:Integer):Integer;    //三个参数
begin
     add3:=a+b+c;
end;

function add4(a,b,c,d:Integer):Integer;    //四个参数
begin
     add4:=a+b+c+d;
end;

function add5(a,b,c,d,e:Integer):Integer;  //五个参数
begin
     add5:=a+b+c+d+e;
end;

function add6:Integer;        //加入一些局部变量
var local1,local2,local3,local4,local5:Integer;
begin
     local1:=1;
     local2:=2;
     local3:=3;
     local4:=4;
     local5:=5;
     add6:=local1+local2+local3+local4+local5;
end;

function add7(a,b,c,d,e:Integer):Integer;  //利用result来返回
begin
     result:=a+b+c+d+e;
end;

function add8(a,b,c,d,e:Integer):Integer;StdCall;//StdCall调用方式
begin
     add8:=a+b+c+d+e;
end;

procedure TForm1.Button1Click(Sender: TObject);
var a,b,c,d,e:Integer;
    s1,s2,s3,s4,s5,s6,s7,s8,s:Integer;
begin
     a:=1; b:=2; c:=3; d:=4; e:=5;
     s1:=add1(a);
     s2:=add2(a,b);
     s3:=add3(a,b,c);
     s4:=add4(a,b,c,d);
     s5:=add5(a,b,c,d,e);
     s6:=add6;
     s7:=add7(a,b,c,d,e);
     s8:=add8(a,b,c,d,e);
     s:=s1+s2+s3+s4+s5+s6+s7+s8;      //必须要有这么几句
     MessageDlg(IntToStr(s),mtConfirmation,[mbOK],0);  //不然编译器根本不去处理返回值
end;

用DEDE反一下看看,这个Button1Click的内容:
代码:
004403EC   55                     push    ebp
004403ED   8BEC                   mov     ebp, esp
004403EF   83C4D8                 add     esp, -$28    ;空出地方放局部变量
004403F2   53                     push    ebx
004403F3   56                     push    esi
004403F4   57                     push    edi
004403F5   33C9                   xor     ecx, ecx
004403F7   894DD8                 mov     [ebp-$28], ecx
004403FA   33C0                   xor     eax, eax
004403FC   55                     push    ebp

* Possible String Reference to: '関-?腽_^[嬪]?
|
004403FD   68E9044400             push    $004404E9

***** TRY
|
00440402   64FF30                 push    dword ptr fs:[eax]  ;这是DELPHI的例行公事
00440405   648920                 mov     fs:[eax], esp    ;据我观察只要调用VCL库的都要SEH
00440408   BB01000000             mov     ebx, $00000001  ;a:=1
0044040D   BE02000000             mov     esi, $00000002  ;b:=2
00440412   BF03000000             mov     edi, $00000003  ;c:=3
00440417   C745FC04000000         mov     dword ptr [ebp-$04], $00000004  ;d:=4
0044041E   C745F805000000         mov     dword ptr [ebp-$08], $00000005  ;e:=5

可以看出DELPHI的确不一样,把EBX,ESI,EDI能用的寄存器全都用上了,实在不行了才用[ebp-xx],
从下面的分析中也能看出这一点,DELPHI在能用寄存器时决不用堆栈。

00440425   8BC3                   mov     eax, ebx    ;这是add1的参数啦,不用PUSH的

* Reference to : TForm1.Proc_00440360()
|
00440427   E834FFFFFF             call    00440360    ;CALL add1

{
  00440360   03C0                   add     eax, eax
  00440362   C3                     ret      ;这样的确很快哟
}

0044042C   8945F4                 mov     [ebp-$0C], eax  ;s1:=add1(a)
0044042F   8BD6                   mov     edx, esi    ;add2的参数EDX=2
00440431   8BC3                   mov     eax, ebx    ;add2的参数EAX=1

* Reference to : TForm1.Proc_00440364()
|
00440433   E82CFFFFFF             call    00440364    ;CALL add2

{
  00440364   03D0                   add     edx, eax  
  00440366   8BC2                   mov     eax, edx
  00440368   C3                     ret
}

00440438   8945F0                 mov     [ebp-$10], eax  ;s2:=add2(a,b)
0044043B   8BCF                   mov     ecx, edi    ;add3的参数ECX=3
0044043D   8BD6                   mov     edx, esi    ;EDX=2
0044043F   8BC3                   mov     eax, ebx    ;EAX=1

* Reference to : TForm1.Proc_0044036C()
|
00440441   E826FFFFFF             call    0044036C    ;CALL add3

{
  0044036C   03D0                   add     edx, eax
  0044036E   03CA                   add     ecx, edx
  00440370   8BC1                   mov     eax, ecx
  00440372   C3                     ret
}

00440446   8945EC                 mov     [ebp-$14], eax  ;s3:=add3(a,b,c)
00440449   8B45FC                 mov     eax, [ebp-$04]  ;[EBP-4]=4
0044044C   50                     push    eax      ;终于看见PUSH了噢
0044044D   8BCF                   mov     ecx, edi    ;ECX=3
0044044F   8BD6                   mov     edx, esi    ;EDX=2
00440451   8BC3                   mov     eax, ebx    ;EAX=1

* Reference to : TForm1.Proc_00440374()
|
00440453   E81CFFFFFF             call    00440374    ;CALL add4

{
  00440374   55                     push    ebp
  00440375   8BEC                   mov     ebp, esp  ;这是C里面的方式啦
  00440377   03D0                   add     edx, eax
  00440379   03CA                   add     ecx, edx
  0044037B   034D08                 add     ecx, [ebp+$08];[EBP+8]本来是第一个参数的
  0044037E   8BC1                   mov     eax, ecx  ;这里[EBP+8]是第四个参数
  00440380   5D                     pop     ebp
  00440381   C20400                 ret     $0004
}

00440458   8945E8                 mov     [ebp-$18], eax  ;s4:=add4(a,b,c,d)
0044045B   8B45FC                 mov     eax, [ebp-$04]  ;[EBP-4]=4
0044045E   50                     push    eax      ;注意:先压进去的是第四个参数
0044045F   8B45F8                 mov     eax, [ebp-$08]  ;[EBP-8]=5
00440462   50                     push    eax      ;再压进第五个参数,Pascal从左至右
00440463   8BCF                   mov     ecx, edi    ;ECX=3
00440465   8BD6                   mov     edx, esi    ;EDX=2
00440467   8BC3                   mov     eax, ebx    ;EAX=1

* Reference to : TForm1.Proc_00440384()
|
00440469   E816FFFFFF             call    00440384    ;CALL add5(a,b,c,d,e)
0044046E   8945E4                 mov     [ebp-$1C], eax  ;s5=add5(a,b,c,d,e)

* Reference to : TForm1.Proc_00440398()
|
00440471   E822FFFFFF             call    00440398    ;add6 看看DLEPHI怎么处理局部变量

{
  00440398   53                     push    ebx
  00440399   56                     push    esi
  0044039A   B801000000             mov     eax, $00000001
  0044039F   BA02000000             mov     edx, $00000002
  004403A4   B903000000             mov     ecx, $00000003
  004403A9   BB04000000             mov     ebx, $00000004
  004403AE   BE05000000             mov     esi, $00000005;哈哈,果然不出所料
  004403B3   03D0                   add     edx, eax  ;它用上了一切能用的寄存器
  004403B5   03CA                   add     ecx, edx  ;各位可以试试加上十来个局部变量
  004403B7   03D9                   add     ebx, ecx  ;看它能坚持到几时
  004403B9   03F3                   add     esi, ebx
  004403BB   8BC6                   mov     eax, esi
  004403BD   5E                     pop     esi
  004403BE   5B                     pop     ebx
  004403BF   C3                     ret
}

00440476   8945E0                 mov     [ebp-$20], eax
00440479   8B45FC                 mov     eax, [ebp-$04]
0044047C   50                     push    eax
0044047D   8B45F8                 mov     eax, [ebp-$08]
00440480   50                     push    eax
00440481   8BCF                   mov     ecx, edi
00440483   8BD6                   mov     edx, esi
00440485   8BC3                   mov     eax, ebx

* Reference to : TForm1.Proc_004403C0()        ;我想看看用result是不是有不同
|
00440487   E834FFFFFF             call    004403C0    ;其实和add5一样的,不写了
0044048C   8945DC                 mov     [ebp-$24], eax
0044048F   8B45F8                 mov     eax, [ebp-$08]
00440492   50                     push    eax      ;PUSH 5
00440493   8B45FC                 mov     eax, [ebp-$04]
00440496   50                     push    eax      ;PUSH 4
00440497   57                     push    edi      ;PUSH 3
00440498   56                     push    esi      ;PUSH 2
00440499   53                     push    ebx      ;PUSH 1

* Reference to : TForm1.Proc_004403D4()
|
0044049A   E835FFFFFF             call    004403D4  ;这个眼熟的吧,从右至左的StdCall方式

{
  004403D4   55                     push    ebp
  004403D5   8BEC                   mov     ebp, esp
  004403D7   8B4508                 mov     eax, [ebp+$08]
  004403DA   03450C                 add     eax, [ebp+$0C]
  004403DD   034510                 add     eax, [ebp+$10]
  004403E0   034514                 add     eax, [ebp+$14]
  004403E3   034518                 add     eax, [ebp+$18]
  004403E6   5D                     pop     ebp
  004403E7   C21400                 ret     $0014    ;我还是觉得这样好看一些
}

* Reference to Form1
|
0044049F   8B5DF4                 mov     ebx, [ebp-$0C]
004404A2   035DF0                 add     ebx, [ebp-$10]
004404A5   035DEC                 add     ebx, [ebp-$14]
004404A8   035DE8                 add     ebx, [ebp-$18]
004404AB   035DE4                 add     ebx, [ebp-$1C]
004404AE   035DE0                 add     ebx, [ebp-$20]
004404B1   035DDC                 add     ebx, [ebp-$24]
004404B4   03D8                   add     ebx, eax    ;加起来

........下面的不写了,还值得一提的是在最后DELPHI总要弄出两个RET来,跳来跳去的,也算是DELPHI的特色吧。

上面讲的是自己定义的函数,要是用VCL库的东东,有时候更加莫名其妙一些。看例子:

建一个FORM,放一个BUTTON,一个EDIT,代码如下:
代码:
procedure TForm1.Button1Click(Sender: TObject);
begin
     MessageDlg(edit1.text,mtConfirmation,[mbOK],0);
end;

呵呵太简单了是不是,用DEDE反下:(只写了关键部分)
代码:
004417BE   6A00                   push    $00    ;这是下面MessageDlg的第四个参数,找到没
004417C0   8D55FC                 lea     edx, [ebp-$04];??这是什么??

* Reference to control TForm1.Edit1 : TEdit
|
004417C3   8B83C8020000           mov     eax, [ebx+$02C8]  ;这是下面GetText的参数TControl吧
                ;看上面的Reference
* Reference to: controls.TControl.GetText(TControl):TCaption;
|
004417C9   E8D619FEFF             call    004231A4  ;得到EDIT的文本
004417CE   8B45FC                 mov     eax, [ebp-$04];这是参数一要显示的字串放入EAX
004417D1   668B0D00184400         mov     cx, word ptr [$00441800];这应该是参数二mtConfirmation
004417D8   B203                   mov     dl, $03  ;这是参数三[mbOK]

* Reference to: Dialogs.Proc_00441380
|
004417DA   E8A1FBFFFF             call    00441380  ;这个是MessageDlg
004417DF   33C0                   xor     eax, eax

如果按照上面的分析,看到GetText这里应该只有一个参数就是放入EAX的那个[ebx+02c8],从参考也可以看到这就是EDIT1,可是函数的返回值呢?刚执行完这个CALL后EAX中是没有的,mov eax,[ebp-04]后才出现了,返回值原来在[ebp-4]中。再向上找有一个莫名其妙的lea edx,[ebp-04],按照我上面的分析这应该表示GetText的第二个参数。可是GetText只有一个参数呀。frown.gif
这种需要返回一个比较大的结构的函数,在VC中常用的方法是把一个指针当参数传递过去,而DLEPHI中我猜是不是做成一个隐藏的参数,像上面的GetText表面上看是返回一个TCaption,实际这个并不是放在EAX里返回来的。

总结一下:DELPHI对参数的传递是尽可能多地利用寄存器,一般第一个参数用EAX,第二个参数用EDX,第三个参数用ECX,多于三个参数的时候,对多出来的参数按照从左至右的PASCAL方式来压栈。
对于函数的返回值,有时尽管声明中说它的返回值是TCaption之类等,实际上并没有在EAX中返回,而是在保存一个隐藏的参数中,等需要时再复制过来。(这一点是猜想而已,如果哪位高人知道的话还请指点。反正我以前都是糊里糊涂地跟,结果出来就算了。其实仔细一分析还有点意思。)

转载于:https://www.cnblogs.com/okwary/archive/2008/12/24/1361330.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值