• 关于Delphi的子类化控件消息淫荡法则之一


        囧囧的一年又过去了,没赚到什么钱,也没学到什么很新鲜的东西,还在码农,还在用Delphi,还在各个群,各个论坛中YY,还在和小盆友们打Dota,但是还是打的很烂.作为屌丝码农,我想说,我可以成为Dota中的神一般的后期么?可是可是..........,可是个OOXX,毛线的可是,总之后期还未发育成熟,神装还得继续.努力发展,赶紧出个点金手.点金点金............

        哎!YY远了,作为屌丝,还是回到屌丝的点上来把,给这过去的一年补上这最后一篇博客.Delphi的各种技术内容点,和其他科目点一样,很多,网上各种各样的资料点也很多很多,个人一贯的原则,是以新、奇、怪出发,固原根本为主,所以一直以来,也没啥好写的。因为总是很多别人都写过了的东西,都可以找得到,所以就没必要写了,记录一些一般难以找到的资源,可能会更有意义,所以今年基本上没什么博客内容更新(没办法的事情,屌丝码农,要生活,生活中用到的东西,多数业务为主,奇淫怪巧的东西就少了)。

      正题开始,先说一下子类化的问题!所谓的子类化,网上有很多说明,我就说我个人的随意理解,可能有误,请列位看官斟酌理解。所谓子类化,个人理解就是拦截某个控件的消息以及样式,来进行自己的特定处理以达到特殊的功能需求。这个子类化,可以有子类化别人的程序的控件,也有子类化自己程序的控件。子类化别人的,就需要注入到别人的程序内部,然后做对应处理拦截,我这里主要针对的是自己程序的处理。这个就比较简单了,有API函数

    SetWindowLong,用这个函数,就可以拦截某WinControl的Wndproc窗口过程了。在Delphi中,所有的消息处理,实际上都是用Application来代理处理以及转发派遣的,所以,不必要SetWindowLong,就可以拦截所有控件的消息,Application.OnMessage中处理就可以,比较容易,不过,这个消息事件中有个比较蛋疼的,就是控件的释放消息WM_Destroy,里面捕捉不到。所以,可以考虑到用SetWindowLong替换掉窗口过程,在这个新窗口过程中处理这消息。替换的方式是

    SetWindowLong(ControlHandle,GWL_WNDPROC,newProc);

    这个newProc过程是function NewWndProc(hwnd: THandle;msg: UINT;wparam: wParam;lParam: LParam);integer;stdcall;

    就可以这样

    function NewWndProc(hwnd: THandle;msg: UINT;wparam: wParam;lParam: LParam);integer;stdcall;
    
    begin
    
    end;

    然后SetWindowLong(ControlHandle,GWL_Wndproc,LongInt(@NewWndproc));

    但是这样处理,就是一个全局函数,如果要通用,就要专门定义一个全局变量,用来代理处理

    我这里说的一种方式,就是直接在一个类当中来处理,我想这样说,应该很多人都会说,很容易了,Delphi自带的有一个函数

    MakeObjectInstance,用这个函数,就可以将这个窗口函数定义到类内部来使用,

    对应方式就是

    type
      TTest = class
      private
        FP,OldP: Pointer;
        procedure NewProc(var msg: TMessage);
      public
        constructor Create;    
        destructor Destroy;
        procedure HookWNdproc(hwnd: THandle);
      end;
    
    
    constructor TTest.Create;
    begin
      Fp := MakeObjectInstance(NewProc);  
    end;
    
    destructor Destroy;override;
    begin
      FreeObjectInstance(Fp );
      inherited;
    end;
    
    procedure TTest.HookWNdproc(hwnd: THandle);
    begin
      OldP := Pointer(SetWindowLong(hwnd,GWL_Wndproc,Fp));
    end;

    这里,应该明白的人,就已经知道了,NewProc过程中,没有控件的句柄传递过来,这个就是Delphi处理过了,目的是无句柄的消息派遣传递,也可以用这个来处理的。中间的问题就出在MakeObjectInstance这个函数中,本函数的内容,可以自行去Delphi中看,我就不弄上来了。函数的目的是将控件的窗口过程Hook导向了Delphi的一个内部处理函数StdProc,这个函数在Classes单元中,是上面声明的标准的窗口过程函数
    function NewWndProc(hwnd: THandle;msg: UINT;wparam: wParam;lParam: LParam);integer;stdcall;

    那么我们就可以知道,实际上NewProc实际上调用的还是StdProc这个函数,那么既然如此,那么就肯定还是能够获得里面传递过来的参数的。那么这里就涉及到了程序的函数调用的一个原理,这个东西,实际上在汇编课程中,应该会讲到,就算不讲,自己反一下Delphi的源码就可以看出来,函数调用初期,进入函数的时候,都会有对应的

    push ebp
    mov  ebp,esp

    这样的语句,这个Push ebp目的就是压入上一次的函数环境的堆栈,以便于函数调用完成之后,能够顺利返回,所以,从这里,我们就可以知道上一次函数的调用堆栈在ebp中,而上一个函数就是function NewWndProc(hwnd: THandle;msg: UINT;wparam: wParam;lParam: LParam);integer;stdcall;这个函数了,那么知道了他的调用堆栈,获取堆栈中的参数就很容易了咯,可以来看NewWndProc的堆栈情况,Windows的Stdcall回调函数的参数传递方式是从从右往左进栈,参数压栈之后还会压入一个现场,所以可以知道Hwnd的参数就在这个现场后面,那么就可以知道,这个Hwnd的值了

    代码如下:

    procedure TTest.NewProc(var msg: TMessage);
    var
      controlHandle: THandle;
    begin
      asm
        mov  edx,[ebp] //stdproc个函数的堆栈顶
        mov  edx,[edx+8] //Hwnd参数,参数之后压入了一个现场,所以+8
      end;
    end;

    那么这里就可以用

    type
      TTest = class
      private
        FP,OldP: Pointer;
        procedure NewProc(var msg: TMessage);
      public
        constructor Create;    
        destructor Destroy;
        procedure HookWNdproc(hwnd: THandle);
      end;
    
    procedure TTest.NewProc(var msg: TMessage);
    var
      ControlHandle: THandle;
    begin
      asm
        mov  edx,[ebp]
        mov  edx,[edx+8]
        mov  controlHandle,edx
      end;
      //通过ControlHandle来判定控件,做通用处理
    end;
    
    constructor TTest.Create;
    begin
      Fp := MakeObjectInstance(NewProc);  
    end;
    
    destructor Destroy;override;
    begin
      FreeObjectInstance(Fp );
      inherited;
    end;
    
    procedure TTest.HookWNdproc(hwnd: THandle);
    begin
      OldP := Pointer(SetWindowLong(hwnd,GWL_Wndproc,Fp));
    end;

    于是,这个淫荡的法则,完成了。

    作者:不得闲
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原
    文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    973. K Closest Points to Origin
    919. Complete Binary Tree Inserter
    993. Cousins in Binary Tree
    20. Valid Parentheses
    141. Linked List Cycle
    912. Sort an Array
    各种排序方法总结
    509. Fibonacci Number
    374. Guess Number Higher or Lower
    238. Product of Array Except Self java solutions
  • 原文地址:https://www.cnblogs.com/DxSoft/p/2854759.html
Copyright © 2020-2023  润新知