1.
一个函数/过程里.有几个参数.怎么知道?每个参数的类型又是什么...都要如何知道...代码实现.2.类中取函数:
    如有:
  ta=class
    public
sum(x,y:Integer):integer;
    end  
  那根据 字符串"sum"能知道函数sum的存在?  如果知道..又如何根据 字符串"sum" 调用 sum函数吗?

解决方案 »

  1.   

    潜水状态下, CSDN竟然不给涨每天10分可用分. 回非技术贴, 升可用分.
      

  2.   

    我不是高手,在高手开口之前我先抛砖引玉一下:问题1、“一个函数/过程里.有几个参数.怎么知道?”    如果是问编译器是怎么知道的,这我只能猜测编译器拿到PASCAL源码后,是通过逗号和分号来区分参数的,其实就是分析字符串。估计楼主不是问编译器是怎么知道的。
        但是,一个程序被编译后,它就是机器码,根本没有“参数”和“变量”的概念。如果非要说“参数”,那用汇编助记符翻译机器码,如:004521C7 BAE0214500       mov edx,$004521e0
    004521CC 8BC3             mov eax,ebx
    004521CE E835FFFFFF       call TForm1.Test
    (这几句汇编助记符即下面的程序中的TForm1.Button2Click过程的X:=Test('OK!');这句。返回值的处理在子程序里)第一句,是把一个常量内存区的地址交给edx寄存器,这个地址是一个字符串。
    第二句,应该是把Self指针交给eax寄存器。
    第三句,是进入TForm1.Test汇编子程序。
    上面的第一二句可以理解为“参数处理”。在子程序里,将用到这两个寄存哭的值。超过三个以上的参数,以及stdcall方式传参的话,将通过栈传递,道理是一样的。    所以说,我的理解,问题1基本无意义,因为程序在编译后就是一成不变的了,几个参数,参数是什么类型,都是固定的了(机器码)。-------------问题2、以下演示,只是演示使用MethodAddress方法。如果是通过类名字符串使用类,那至少这个类要注册。unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        function Test(A : string): boolean;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;implementation{$R *.dfm}function TForm1.Test(A : string): boolean;
    begin
      Showmessage(A);
      Result := True;
    end;procedure TForm1.Button1Click(Sender: TObject);
    var
      MN : ShortString;
      P : Pointer;
      F : function(Self : TObject; A : string): boolean;
    begin
      MN := 'Test';
      P := Self.MethodAddress(MN);
      if P <> nil then
        begin
        @F := P;
        F(Self, 'OK?');
        end else Showmessage('没有这个方法');
    end;procedure TForm1.Button2Click(Sender: TObject);
    var X : boolean;
    begin
      X := Test('OK!');
    end;end.
      

  3.   

    可以把上面的Button1Click过程简化一下:procedure TForm1.Button1Click(Sender: TObject);
    var
      F : function(Self : TObject; A : string): boolean;
    begin
      @F := Self.MethodAddress('Test');
      if @F <> nil then F(Self, 'OK?')
      else Showmessage('没有这个方法');
    end;
      

  4.   

    http://www.delphibbs.com/delphibbs/dispq.asp?lid=3804925是这样的...但那个函数的最终返回值,,现在对我来说是有些问题.....
      

  5.   

    大体看了你在delphibbs的贴子。简单的事情不要想复杂了。把你的实际需要描述出来,让大家给你想办法。不过我最近没有时间参与讨论了,只能提供前面的一些思考。Win32 DLL 与调用它的进程共用代码段,与进程内的函数没有什么太大的区别。只要遵守一些必要的安全做法,比如由调用者还是被调者分配空间等,就可以了。江湖上的功夫,既有“少林拳”“降龙十八掌”这样的正宗功夫,也有“九阴真经”“葵花宝典”这样的邪门功夫。都很厉害。但建议不要在一个冷门上投入太大精力。那个用内联汇编的文章,也看了,前些日子也用汇编研究过类似问题,感觉不够安全。(本人言论仅供参考)用正统的方法一样可以解决问题,虽然看起来不够“酷”。
      

  6.   

    上面提到的那个贴子, 参见:
    http://community.csdn.net/Expert/topic/5550/5550177.xml?temp=.3402826
      

  7.   

    function f(n1,n2,n3,n4 : Integer) : Integer;
    var
      i1,i2,i3,i4 : Integer;
    begin
      asm
        mov eax,$f1
        mov ecx,$f2
        mov edx,$f3    mov i1,eax
        mov i2,ecx
        mov i3,edx
      end;  ShowMessage(Format('%x %x %x %x',[i1,i2,i3,i4]));
      ShowMessage(Format('%x %x %x %x',[n1,n2,n3,n4]));
    end;procedure TForm1.Button1Click(Sender: TObject);
    var
      i : Integer;
    begin
      i := f(1,2,3,4);
    end;
      

  8.   

    在函数的begin处加上断点,然后打开CPU窗口
    Unit1.pas.49: i := f(1,2,3,4);
    00452A84 6A04             push $04
    00452A86 B903000000       mov ecx,$00000003
    00452A8B BA02000000       mov edx,$00000002
    00452A90 B801000000       mov eax,$00000001
    00452A95 E8DAFEFFFF       call f
    Unit1.pas.28: begin
    00452974 55               push ebp
    00452975 8BEC             mov ebp,esp
    00452977 83C4C4           add esp,-$3c
    0045297A 53               push ebx
    0045297B 56               push esi
    0045297C 57               push edi
    0045297D 33DB             xor ebx,ebx
    0045297F 895DC4           mov [ebp-$3c],ebx
    00452982 895DE8           mov [ebp-$18],ebx
    00452985 8BF9             mov edi,ecx
    00452987 8BF2             mov esi,edx
    00452989 8BD8             mov ebx,eax
    0045298B 33C0             xor eax,eax
    0045298D 55               push ebp
    0045298E 68572A4500       push $00452a57
    00452993 64FF30           push dword ptr fs:[eax]
    00452996 648920           mov fs:[eax],esp
      

  9.   

    从上面的代码可以看出
    函数在执行begin时,已经将参数从edx,ecx,eax中放到了其它寄存器
    在begin以后,edx,ecx,eax存放的已经不是参数了
    即使能从edx,ecx,eax中得到东西,也仅仅是巧合而已
      

  10.   

    to : 大头鸟那是从哪里得到的啊?(以register约定传参为例解释一下) ^_^
      

  11.   

    lZ的第一个问题:
    “函数”“过程”是高级语言的特性,
    参数个数是否必须固定,是否强制类型检查都是由语言本身约定的,
    参数个数检查、类型检查这些工作都是由编译器完成,
    编译器如何得知参数个数,如何进行类型检查,只有作者最清楚
    目前还没听说Delphi提供了这种能让我们检查参数个数的机制LZ第二个问题:
    对于类中的成员方法,是可以通过名字去执行这个成员方法的
    lihuasoft(坐井观天)已经回答了
      

  12.   

    可能有点误会大头鸟的意思。我在8楼的意思是:汇编子程序需要处理某些数据,这些数据可以理解为“参数”,这些“参数”就是通过eax,edx,ecx或栈传递的。我在13、14楼的意思是在从另一个角度描述寄存器传递和栈传递。大头鸟兄还在吗?今晚讨论一下。明天没时间了....
      

  13.   

    to lihuasoft(坐井观天)
    那段反汇编是从我机器上得到的,
    装了cnpack以后,可以将CPU窗口中的代码复制到剪贴板register是delphi的默认调用方式,有没有register都是一样的
      

  14.   

    从上面的代码可以看出
    函数在执行begin时,已经将参数从edx,ecx,eax中放到了其它寄存器
    在begin以后,edx,ecx,eax存放的已经不是参数了
    即使能从edx,ecx,eax中得到东西,也仅仅是巧合而已------------------------不过你这样说, 的确容易误导他.  对于汇编子程序(不是说高级语言的函数和方法)来说,“参数”(应该说子程序需要的数据)的确是通过上述三个寄存器或栈传递的。至于子程序内部又对寄存器进行处理,那是另外一回事了。不是巧合。
      

  15.   

    对于register的参数传递机制,我是没有异议贴这篇代码,主要想证明一下那篇帖子里的8楼伴水的说法
    对于你在那篇帖子最后的代码,是巧合还是必然,还得另外证明不过直觉上巧合的可能要大
      

  16.   

    困了,明天再去看那篇帖子
    在这个帖子里讨论另一个帖子,也不太合适
    -_- zzzzz
      

  17.   

    哦, 好的. ^_^  谢谢一起讨论. 改日有时间了我一定再看本贴及http://community.csdn.net/Expert/topic/5550/5550177.xml?temp=.3402826
      

  18.   

    (冒昧在本贴讨论看似与本贴无关的话题, 实际上是有关的)一、与大头鸟意见一致的是:代码编译后,就没有“变量”“参数”的概念了。所以问题一基本无意义。二、与大头鸟意见不一致的:
      我的意见:对于编译后的机器码,如果翻译为汇编助记符,对于其中的一个汇编子程序来说,子程序需要的数据(可以用“参数”来形容它)是通过寄存器或栈传递到汇编子程序中的。(附:“参数”个数及使用方法已在编译时确定)
      对此,大头鸟在前面举了一个例子,用以说明“子程序内部从eax,edx,ecx中获得正确的‘参数’值只是巧合”。
      我看了一下大头鸟的例子,的确,ShowMessage(Format('%x %x %x %x',[n1,n2,n3,n4]));时,n1,n2,n3,n4值的获得,不是通过上述寄存器,而是通过栈。
      我是这样理解的:Delphi编译器“优先”(注意,是“优先”不是“绝对”!)使用eax,edx,ecx寄存器传参。但Delphi不可能,任何工具也不可能做得比人脑还要智能、灵活;所以,当子程序内部调用Delphi内建函数(很多的,不仅仅Format)时,需要使用eax,edx,ecx等寄存器向内建函数传参,内建函数返回后,这时,Delphi就无法从eax等寄存器中得到参数值了,这样,Delphi就无法保证“优先”使用寄存器传参了,于是改为栈传递。可以看一下,ShowMessage(Format('%x %x %x %x',[n1,n2,n3,n4]));时,n1-n4的值应该是通过栈顶偏移获得的。
      大头鸟做的是在使用参数前,人为地破坏三个寄存器的现值。这与我说的调用内建函数占用寄存器,是一样的效果。
      我的理解,这并不能说明Delphi过程的参数不是通过寄存器和栈传递的。只能说,Delphi“优先”使用寄存器传参,无法保证“优先”时,才使用栈。  Delphi能够保证“优先”使用寄存器传参时,不是“巧合”。  另外,关于我那个相关贴子里最后几层的代码,需要注意,我用的是全汇编。另外,前面本贴我用马甲回贴时已有“不安全”之说。^_^
      我是主张尽量不要在Delphi里用汇编的(很可能我们写的汇编代码不如Delphi生成的汇编代码简洁高效)
      

  19.   

    其实, 大头鸟在前面的示例程序, 没有说服力. 不能说明他的论点. 以下是f函数的汇编码: begin
          push ebp
          mov ebp,esp
          add esp,-$3c
          push ebx         '注意这里三个入栈操作
          push esi         '这是为了后面要使用这三个
          push edi         '寄存器暂存前三个参数值(From eax,edx,ecx)
          xor ebx,ebx
          mov [ebp-$3c],ebx
          mov [ebp-$18],ebx
          mov edi,ecx      '因为下面要人为地破坏
          mov esi,edx      'eax,edx,ecx的值,所以
          mov ebx,eax      '此处做了值备份(即大头鸟说的“函数在执行begin时,已经将参数从edx,ecx,eax中放到了其它寄存器”)      xor eax,eax
          push ebp
          push $00452a57
          push dword ptr fs:[eax]
          mov fs:[eax],esp
          mov eax,$000000f1      '开始进行人为破坏
          mov ecx,$000000f2      '
          mov edx,$000000f3      '破坏完毕
          mov [ebp-$04],eax      '对位于栈上的局部变量赋值
          mov [ebp-$08],ecx      '
          mov [ebp-$0c],edx      '
          //此处略去ShowMessage(Format('%x %x %x %x',[i1,i2,i3,i4]));的汇编码
          //在此期间未用到edi,esi,ebx
          //以下是ShowMessage(Format('%x %x %x %x',[n1,n2,n3,n4]));
          lea eax,[ebp-$3c]
          push eax
          mov [ebp-$38],ebx    '注意! 从ebx取值! ebx值从何来? 返回看一下mov ebx,eax那句
                             //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
          mov byte ptr [ebp-$34],$00
          mov [ebp-$30],esi    '注意! ....
          mov byte ptr [ebp-$2c],$00
          mov [ebp-$28],edi    '注意! ....
          mov byte ptr [ebp-$24],$00
          mov eax,[ebp+$08]    '这是第四个参数, 直接栈顶偏移获得
          mov [ebp-$20],eax
          mov byte ptr [ebp-$1c],$00
          lea edx,[ebp-$38]    '下面开始准备进入Format子程序了
          mov ecx,$00000003    '...
          mov eax,$004521d8    '...
          call Format
          mov eax,[ebp-$3c]    '下面开始准备进入ShowMessage子程序了
          call ShowMessage
          //....下面的出栈等操作略可见, 前三个参数值的的确确是通过这三个寄存器传入了函数。传入后,eax,edx,ecx每时每刻都有每时每刻的用处。在需要备份其值以备后用时,Delphi会为其暂存在其他寄存器中的。
      

  20.   

    长久没来CSDN Delphi版了,终于又看到有点技术实力的帖子了
      

  21.   

    为了能和前面的讨论接上,再占用一下贵宝地,LZ勿怪我想认识上都是差不多的,问题是出在表述和理解1、register参数传递机制上,认识是一致的:
    使用register传递参数时,将前三个参数放入eax,ecx,edx,其余参数从左到右入栈
    这一过程,是发生在函数被调用的时候i := f(i1,i2,i3,i4);2、在函数的begin执行时,eax,ecx,edx的数据会被保存到其它寄存器,
    而eax,ecx,edx本身则被“挪用”做别的事情
    在函数内部,期望从eax,ecx,edx中取得参数值的做法,是不可靠的,
    因为我们无法保证不会产生类似于
    asm
        mov eax,$f1
        mov ecx,$f2
        mov edx,$f3
    这种改写eax,ecx,edx的动作
    某些情况下我们之所以能从eax,ecx,edx中取得参数值,
    仅仅是因为编译器“不想理会”eax,ecx,edx,
    设想一下:哪天编译器心情好了,去改一下eax,ecx,edx
    我们从eax,ecx,edx中取得参数值的代码立即会失效
    所以我认为函数内部能够从eax,ecx,edx中取得参数值,仅仅是“巧合”
    不能作为一个“办法”3、这篇帖子有一个问题和那篇帖子相关,
    就“任意类型”以及“可变数量”参数的函数问题
    我的观点是:使用variant、无类型参数、指针,可以使Delphi函数支持“任意类型”参数的
    而函数参数“可变数量”,从BCB和Delphi对Format函数的定义可以看出来
    C/C++支持这种特性,Delphi是不支持的,
    Delphi在语法上就没有提供这种机制,得通过数组一类的模拟
      

  22.   

    补充几点:
    1. MethodAddress只能得到pulished 方法或属性的地址, 否则得不到;
    2. 从汇编码找不到解决方法, 要从RTII(runtime type information) 入手, 该方法是published可以得到的, <<delphi6开发人员指南>>与<<Inside VCL>>中有相关的资料;
    3.反射(reflect)是Java, .net才有的概念在 delphi for win32中没有的.
      

  23.   

    精彩...
    继续>>>>
    而函数参数“可变数量”,从BCB和Delphi对Format函数的定义可以看出来
    C/C++支持这种特性,Delphi是不支持的,
    Delphi在语法上就没有提供这种机制,得通过数组一类的模拟Delphi不是支持吗? Format后面不就是可变数量和类型吗??   array of const
      

  24.   

    提的二个问题  用delphibbs上的方法..都可以达到了..
      

  25.   

    Delphi里的Format实质上是两个参数,
    第二个参数是一个数组用BCB里的Format说明C/C++支持可变参数不太合适
    可以看一下printf,就是一个可变参数的函数
    int printf( const char* format, ...);Delphi对于这种可变参数的特性,没有提供语法层面支持,
    而是在编译器上提供的“特殊支持”,这一点从WriteLn可以证明,
    帮助里是这么写的:
    procedure WriteLn([ var F: Text; ] P1 [, P2, ...,Pn ] );
    但实际上Delphi根本没有这种语法
      

  26.   

    1.
    一个函数/过程里.有几个参数.怎么知道?每个参数的类型又是什么...都要如何知道...代码实现.-----
    * 如果是问编译器如何知道,那当然完全是根据源程序了.
      例如,如果你在程序中写了一句
       writeln(a,b); 
     输出语句,编译器根据该语句括号中的“a,b”可知道是2个参数,
      至于a,b是什么类型,则要查此前的var语句中对变元a,b的说明了。* 如果是一个程序员想用某个函数/过程,但不知道几个参数,它们
     又是什么类型,那当然应该查可靠的资料,如“用户手册”或“联机帮助”。 
    2.类中取函数:    如有:
      ta=class
        public
    sum(x,y:Integer):integer;
        end
      
      那根据字符串"sum"能知道函数sum的存在?  如果知道..又如何根据 字符串"sum" 调用 sum函数吗?
    ---这明显是在问:编译器如何具体实现函数/过程的编译了。
    编译技术虽然有一定繁琐(所以中国至今没有人能开发一个实际可用商品编译器),
    但应该说,其原理实际并不难了解,是万全可以想得通的事情。例如:函数sum存在与否?
    如果sum()是delphi本身提供的函数(系统函数),当然知道;
    如果sum()不是delphi提供的系统函数,则一定是用户自己定义的;
    如果用户自己预先没有定义sum,而在某处使用(调用)了sum,
    则在编译过程中,由于查不到sum的原型,证明是非法调用,就可报错。