第一:
  WMLButtonDblClk是消息处理方法!inherited将调用父类相应的消息处理方法
第二:
  VCL源码是可以设置断点跟踪进去的,方法是调整Option中的查找路径,使
VCL所在路径优先!

解决方案 »

  1.   

    因为你是要拦截消息
    inherited执行原本应该执行的TWMLButtonDblClk消息响应过程。
      

  2.   

    看汇编码看到
    //begin
    push ebp
    mov ebp,esp
    add esp, -$08
    push esi
    mov [ebp - $08], edx  //保存的Message地址 
    mov [ebp - $04], eax  //保存的Self值//SendCancelMode(self)
    mov edx, [ebp - $04]
    mov eax, [ebp - $04]
    call TControl.SendCancelMode// inheritedmov edx, [ebp - $08]      //保存的Message地址
    mov eax, [ebp - $04]     //取得Self
    mov ecx, [eax]           //取得vptr
    call dword ptr [ecx - $10] //-$10在System中正好是 vmtDefaultHandler    = -16;也就是调用DefaultHandler,我用TButton跟踪进入了TWinControl的DefaultHandler
    估计是一个消息如果在祖先中没有对应的响应,编译器就认为Inherit就调用了DefaultHandler
    另外要跟踪进源码
    设一下Project\options\compiler的Use Debug DCUs选上就可
      

  3.   

    问题是,父类中没有发现同名的方法!
    WMLButtonDblClk 
      

  4.   

    知道我跟踪的结果吗???
    inherited;调用的结果是:
    procedure TCustomForm.DefaultHandler(var Message);
    我实在是想不通!!!
      

  5.   

    DefaultHandler是对消息处理的封装,与OP语言本身类的继承好象没有什么关系啊!
    Borland不会为了一个消息处理安排这么一个语言特例吧?难道不能用标准方法来实现吗?
      

  6.   

    消息处理方法本来就是个特例
    看其声明过程  procedure FunctionName();Message WM_MEssage;
                                           ^^^^^^^
    其他过程是没有Message的!
    另外VC和BCB中对消息的处理都是通过消息映射!由此可看出程序对消息处理
    有一定的特殊性!具体是怎么会这样我也不清楚,只是谈点我的看法!
      

  7.   

    消息函数好像有点特殊,它的inherited是调用系统原来的处理函数,然后才轮到你的代码,这样做是有理由的,首先要保证系统优先,处理完了才处理你的。
    当然在特殊的情况下,可以是你的代码优先,甚至可以接管这个消息处理(当然不推荐,除非你认为非常安全)。我的一点理解,但愿对你有用。
      

  8.   

    不用inherited就不会处理默认的处理过程了,你就吃掉了这个消息了   
      

  9.   

    你调试的是在TForm1上的双击事件当然调用的是TCustomForm
    因为TCustomForm重载了DefaultHandler
    procedure DefaultHandler(var Message); virtual;
    在TObject中    procedure DefaultHandler(var Message); virtual;
    这很正常阿
      

  10.   

    那么inherited到底是什么意思呢?继承?WMLButtonDblClk在TControl中,被调用的DefaultHanlder 在TCustomForm中,怎么这么乱啊?是不是中间还有什么代码没有trace进去啊?我发现system.pas中无论如何都无法step into,是不是在里面还有什么内幕?WMLButtonDblClk中的inherited调用DefaultHanlder,它们是怎么联系在一起的呢?
      

  11.   

    我发现所有在TControl中的消息处理句柄,其Inherited调用的都是TCustomForm的DefaultHandler,我想知道这是为什么?类之间有什么默契吗?从inherited这个语句本身来看,根本看不出这个调用的道理啊?
      

  12.   

    TO: zwjchina(蒲石)
    消息处理方法本来就是个特例
    看其声明过程  procedure FunctionName();Message WM_MEssage;
                                          ^^^^^^^这倒并不奇怪,这里是为Dispatch方法准备映射数据,而我要探讨的是继承,为什么一个inherited能有这样的效果,你做几个继承的类出来看看,能不能由一个祖先的方法调用inherited就能调用到后代的某个方法上?这与上面的问题好象没有关系。
      

  13.   

    在Delphi的帮助中:The inherited statement searches backward through the class hierarchy and invokes the first message handler with the same ID as the current method, automatically passing the message record to it. If no ancestor class implements a message handler for the given ID, inherited calls the DefaultHandler method originally defined in TObject.
    The implementation of DefaultHandler in TObject simply returns without performing any actions. By overriding DefaultHandler, a class can implement its own default handling of messages. The DefaultHandler method for VCL controls calls the Windows DefWindowProc function.意思应该是:当消息处理句柄调用inherited而祖先类没有定义处理句柄,则调用祖先的DefaultHandler方法,而TCurstomForm重载了此方法。导致上面的行为,看来得好好研究帮助啊!
      

  14.   

    由此看来,Object Pascal已经和VCL不可分割,不再是一个独立的语言了。就这一点看,Object Pascal好象与C++不是一条路上的了。C++无论VC/CB怎么样,都是独立的,不会有这种语言与类库的契约关系
      

  15.   

    到不知Kylix是如何处理?其类库也是这样的吗?
      

  16.   

    这正是多态的体现阿,下例
    TA = class
      procedure A; virtual;
      procedure B; virtual;
    end;TB = class(TA)
      procedure A; override;
      procedure B; override;
    end;procedure TA.A;
    begin
      B;
    end;procedure TA.B;
    begin
      //
    end;procedure TB.A;
    begin
      Inherited;
    end;procedure TB.B;
    begin
      ShowMessage('TB.B');
    end;
      

  17.   

    inherited除了继承父类的方法外,消息处理中用inherited比较特殊,因为除了该控件以外,可能有别的也要响应该消息,所以用inherited可以把消息处理还给系统
      

  18.   

    TO:xzgyb(老达摩)
    你的例子我理解,其实我是用C++Builder的,多态在C++是用虚函数实现的。我提出的问题主要针对Inherited,现在明白了。
    在C++中没有Inherited语法,调用父类的方法必须显式调用如:
    TA::B();
    这种调用将使编译器放弃虚函数表机制而直接调用,因此无法达到同样的目的,C++中只要直接调用
    B();
    即可,因为如果子类重载了该方法,虚函数表中的指针已经指向了子类的方法。
      

  19.   

    binbin, OK
    我以前也学bcb
    在Delphi中也是一样阿
    只不过我觉得在c++中更自由一些
    没记错的话
    可以调爷爷或辈更大的方法
    而在delphi中只能调父亲的方法
    想调爷爷的方法,我不知道用什么方法调
      

  20.   

    那就让父类再Inherited传给爷爷吧:)
      

  21.   

    关于message handler在inherited上的行为,我在Borland的新闻组上得到了更详细的答复:
    message handler是动态方法,类似虚函数表,Delphi有动态方法表(是不是有点象消息映射表?),在调用时会进行查找,下面是原文,希望对大家有所帮助:WMLButtonDblClk is a message handler, message handlers are dynamic methods. A 
    call to a dynamic method works a little differently than a call to a static 
    or virtual method. For a static method the compiler will simply code a direct 
    call to the methods address. If you use inherited in a static method (which 
    is legal) the compiler will look for a method of the same name and parameter 
    signature in a base class and code a direct call to that method. So the 
    address is resolved at compile-time.For a virtual method the compiler looks up the methods address in the classes 
    virtual method table. This is basically an array of method addresses, each 
    "chain" of virtual methods (the method declared virtual in the base class + 
    all overriden versions in descendent classes) has a fixed index in the VMT, 
    the compiler codes an indirect call to the entry in the VMT. The offset the 
    method address can be found at in the classes VMT is calculated at 
    compile-time but the method address is resolved at run-time. If you call 
    inherited in an overriden virtual method the compiler simply codes an 
    indirect call using the immediate ancestor classes VMT instead of the current 
    objects classes VMT. Each classes VMT starts with a copy of the VMT of its 
    ancestor class, with entries for methods that have been overriden in the 
    class changed to point to the classes methods. For a dynamic method the picture is more complicated still. A classes dynamic 
    methods are listed in another table that is associated with the class record, 
    the dynamic method table. This is basically an array of key/value pairs, the 
    key for a dynamic method is a number (the message ID for a message handler), 
    the value is the methods address. A classes DMT contains only the dynamic 
    methods declared in the class itself, no methods from the ancestor classes. 
    This makes DMTs smaller than VMTs, but calling a dynamic method involves a 
    linear search for a method matching the methods key through a classes DMT and 
    the ancestor classes DMTs, if no match is found in the current class. The 
    compiler will code a call to a run-time library function (_CallDynaInst) when 
    it needs to call a dynamic method indirectly (i.e. if you use inherited in a 
    dynamic method, or Dispatch gets called to deliver a message to the object). 
    _CallDynaInst uses another method _FindDynaInst to perform the search for the 
    method in the classes DMT. If it finds no method matching the key 
    _CallDynaInst will call DefaultHandler instead. If you call inherited in a dynamic method _CallDynaInst will start the search 
    at the immediate base class of the class you have the inherited in. For 
    TControl that means that all inherited calls will end up calling 
    DefaultHandler, since none of the base classes has any message handlers.
      

  22.   

    我来总结一下:
    1。Inherited 关键字一般是调用父类同名方法。
    这里的一般是指,方法是虚方法或者普通方法。
    2。当方法为动态方法,Inherited 会调用 _CallDynaInst
    _CallDynaInst会调用 GetDynaMethod 线性在DMT中寻找索引一致的方法
    如果没有找到,则在父类的DMT中继续查找,一直找到TObject.DefaultHandler
    为止,消息处理函数就是放在DMT中,消息函数的索引就是消息索引,
    其他的动态方法的索引为了避免和同一个类中的消息函数索引冲突,
    一般从$FFFF向下分配,正是zwjchina(蒲石)感觉的那样,消息处理函数,编译器会特殊处理。 
    (这里提醒大家,自定义消息时,消息索引不要让最高位为1,即不要用大于$8000,不易产生冲突)。一句话,动态方法,或者消息处理函数内部,使用没有参数的Inherited时,
    编译器翻译Inherited就是翻译成一个 _CallDynaInst调用。
    而虚方法和普通方法时,编译器在编译期指定同名方法的Call调用。同意hahaxiao()的观点,delphi编译器已经和VCL紧密结合了。to binbin(滨滨,不是二进制):
    “在C++中没有Inherited语法,调用父类的方法必须显式调用如:
    TA::B();
    这种调用将使编译器放弃虚函数表机制而直接调用,因此无法达到同样的目的”呵呵,其实object pascal中这里也没有用到VMT,所以c++显式调用达到的效果是一样的
    原因我上面已经分析过了。system.pas 可以跟踪进去。to xzgyb(老达摩):
    “binbin, OK
    我以前也学bcb
    在Delphi中也是一样阿
    只不过我觉得在c++中更自由一些
    没记错的话
    可以调爷爷或辈更大的方法
    而在delphi中只能调父亲的方法
    想调爷爷的方法,我不知道用什么方法调”和c++中一样的,
    ie TObj = class
       TObj1 = class(TObj)
       TObj2 = class(TObj1)
       Obj2 = TObj2.Create;
       TObj(Obj2).P1;
    这里只要type cast一下就可以了,同样可以调用任何祖先的普通方法。
    如果是虚方法,或者动态方法,可以嵌入汇编,手动写查找VMT,或者DMT的代码
    也一样可以实现执行任意祖先方法。Borland newsgroup这篇文章有一点点错,
    “_CallDynaInst uses another method _FindDynaInst to perform the search for the
                                        ~~~~~~~~~~~~~~这里并没有调用_FindDynaInst,而是调用了GetDynaMethod。
     
    method in the classes DMT. If it finds no method matching the key
    _CallDynaInst will call DefaultHandler instead.” //粗略的写了一下,欢迎大家继续讨论。
      

  23.   

    推荐几本书把,讲底层的:
    Developing Custom Delphi3 Components 2nd.Ed
    (很多国外的delphi牛人喜欢这本书,在他们的书架上都能看到,黄色封皮)
    Delphi in a Nutshell by Ray Lishner
    Secret of Delphi2 by Ray Lishner
    Delphi Developer's Handbook (delphi高级开发指南)
    OPLG(Object Pascal Language Guides),呵呵这本书才是权威,也很有深度,呵呵,delphi光盘中自带,
    不知大家注意过没有。btw:csdn上很少看到这种有价值的问题
      

  24.   

    很感谢nofog的透彻分析,多谢
    希望继续讨论
      

  25.   

    补充:
    3。inherited,后面是个函数方法的话,不能省掉参数。inherited fun(...);//correctinherited; //wrong4。inherited 所要执行的方法是抽象的,则会引起运行时异常。这里是 Peter Below写的一个手动查找DMT的函数
    FindDynamicMethod(aClass.ClassGrandparent, MessageID);用一个TMethod类接收返回值
    可以直接执行祖父消息处理函数(可以屏蔽父亲的处理)function FindDynamicMethod(aClass: TClass; anIndex: SmallInt): Pointer;
    type
      TIndices= Array [1..1024] of SmallInt;
      TProcs  = Array [1..1024] of Pointer;
    var
      pDMT : PWord;
      i, count: Word;
      pIndices : ^TIndices;
      pProcs : ^TProcs;
    begin
      Result := nil;
      if aClass = nil then 
        Exit;
      pDMT := Pointer(aClass);
      // Find pointer to DMT in VMT, first Word is the count of dynamic
      // methods
      pDMT := Pointer(PDword(Integer(pDMT) + vmtDynamicTable)^);
      count := pDMT^;
      pIndices := Pointer(Integer(pDMT) + 2 );
      pProcs   := Pointer(Integer(pDMT) + 2 + count * sizeof(smallint));
      // find handler for anIndex
      for i:= 1 to count do 
        if pIndices^[i] = anIndex then 
        begin
          Result := pProcs^[i];
          Break;
        end;
      if Result = nil then 
        Result := FindDynamicMethod(aClass.Classparent, anIndex);
    end;下面给用BCB的朋友提个问题,呵呵,大家继续讨论。
    c++中没有 inherited ,如果父类方法是个private
    子类中改如何实现调用父类的同名方法。
    比如消息处理的时候,
    void __fastcall TMyEdit::WMChar(TWMKey& Message)
    {
      //想要调用父类 TEdit::WMChar,How to?
      

  26.   

    不好意思,一下子贴了这么长
    谢谢nofog的补充
    void __fastcall TMyEdit::WMChar(TWMKey& Message)
    {
      //想要调用父类 TEdit::WMChar,How to?

    试试在说
    呵呵
      

  27.   

    实在不好意思,昨天凌晨草草的写了点东西
    没有仔细斟酌,调试的时候也没有看清楚,结果错误百出。惭愧啊,惭愧。
    多亏xzgyb(老达摩)指出,感激不尽。的确,我把消息处理和动态方法搞错了。
    消息处理有单独的查找DMT的函数(Dispatch),
    动态方法也是和普通方法一样,生成一个同名方法的call。这里我做一个猜测:message关键字声明的方法,编译器会把他加到当前
    类的DMT中。我没有时间验证了(12个小时后,我就坐火车回家了)。
    留给大家。至于inherited执行的是个abstract方法,引起异常,这已经涉及到了
    delphi复杂的异常处理机制了。暂时放一下了。to xzgyb(老达摩):多谢,我们交个朋友把 :)
      

  28.   

    不好意思,nofog,白天我上不了网,才看到
    交个朋友,求之不得了
    呵呵
    可能现在一回家了吧
    今天是小年了,在这里也祝所有的兄弟小年快乐
    呵呵