关于Delphi的子类化控件消息淫荡法则之一
发布日期:2022-03-13 05:36:12 浏览次数:8 分类:技术文章

本文共 3538 字,大约阅读时间需要 11 分钟。

    囧囧的一年又过去了,没赚到什么钱,也没学到什么很新鲜的东西,还在码农,还在用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;beginend;

然后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 ebpmov  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;

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

 

转载于:https://www.cnblogs.com/DxSoft/archive/2013/01/10/2854759.html

转载地址:https://blog.csdn.net/weixin_30323961/article/details/98235033 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Filter的应用--权限过滤
下一篇:格式化文本

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年03月23日 12时01分03秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

VMWare虚拟机下Fedora30升级Fedora31,重启后无法启动系统,出现alloc magic is broken at 0xXXXX的错误 2019-04-26
【解决方案】STM32L152单片机驱动段码LCD屏,执行HAL_LCD_Init函数失败返回HAL_TIMEOUT,长时间卡在LCD_FLAG_RDY的while循环里面的解决办法 2019-04-26
【方法】STM32F103C8单片机在Keil 5环境下使用C++编写程序,并将printf和cout重定向到串口 2019-04-26
【STemWin】STM32F429IG单片机用LTDC驱动正点原子7寸RGB彩色触摸屏,并裸机移植STemWin图形库 2019-04-26
【经验分享】调试STM32F107VC单片机驱动DP83848以太网PHY芯片时遇到的问题 2019-04-26
【程序】STM32F107VC单片机驱动DP83848以太网PHY芯片,移植lwip 2.1.2协议栈,并加入网线热插拔检测的功能(HAL库) 2019-04-26
【BUG处理】STM32F1和F2单片机上用HAL库的USART串口接收函数HAL_UART_Receive_IT循环接收串口字符,串口接收大批量数据后突然死机,不能继续接收的解决办法 2019-04-26
在PCB板上调试104(0.1μF)独石电容驱动MAXIM MAX3232串口芯片的心得 2019-04-26
【方法】STM32 FreeRTOS系统errno变量做到线程安全的方法 2019-04-26
【程序】STM32F407VE单片机驱动两片TJA1050 CAN收发器并进行双CAN通信 2019-04-26
【解决方案】STM32F107VC单片机下运行STM32CubeMX生成的USB_OTG Mass Storage工程,无法识别USB设备的解决办法 2019-04-26
【解决方案】STM32F103C8单片机运行CubeMX生成的CDC虚拟串口的程序,设备能枚举成功但不能启动的解决办法 2019-04-26
07.保护模式下字符显示 2019-04-26
08.C语言绘制系统界面 2019-04-26
09.调色板绘制系统界面 2019-04-26
Ubuntu sudo apt-get install *** 无法安装软件 2019-04-26
10.绘制ascii字体/鼠标指针 2019-04-26
11.建立中断机制 2019-04-26
12.键盘中断处理 2019-04-26
AES CBC加密/解密 2019-04-26