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;

于是,这个淫荡的法则,完成了。
Delphi的子类化控件消息, 消息子类化的更多相关文章
- VC控件-子类化控件技术
子类化一个Windows控件与子类化一个C++类不同,子类化一个控件要求你把一个窗口的一些或所有的消息映射都替换成自己的函数来响应,这样你就有效的阻止了控件去做系统默认的行为,而按自己的想法去做.子类 ...
- 眼见为实(2):介绍Windows的窗口、消息、子类化和超类化
眼见为实(2):介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较“生僻”的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和 ...
- TControl.WMLButtonUp的inherited的作用——是为了给子类控件新的处理消息的机会
意外注意到这个小细节: procedure TControl.WMLButtonUp(var Message: TWMLButtonUp); begin inherited; // 注意,如果是直接点 ...
- delphi 使用工控机控件 iThreadTimes 出现问题, 导致主程序创建页面的时候, 阻塞消息, 不能正常执行。
delphi 使用工控机控件 iThreadTimes 出现问题, 导致主程序创建页面的时候, 阻塞消息, 不能正常执行. 使用这个控件需要小心 function Tfrm_MainIPC.Open ...
- Delphi中SendMessage使用说明(所有消息说明) good
Delphi中SendMessage使用说明 SendMessage基础知识 函数功能:该函数将指定的消息发送到一个或多个窗口.此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回.而函数Po ...
- Delphi实现获取句柄并发送消息的方法(FindWindow、FindWindowEx、EnumChildWindows、SendMessage)
Delphi实现获取句柄并发送消息的方法 本文以实例形式详细说明了Delphi获取句柄并发送消息的方法,具体用法说明如下: 查找另外一个窗口的句柄: handle := FindWindow(nil, ...
- 如何给对话框中的控件发送消息呢?Windows消息分类
以博文CTabCtrl中介绍的那样,给Tab添加子对话框来显示Tab内容.那么如果这个子对话框中含有个CTreeCtrl控件,有个Button控件,我想要模拟给这两个控件发送消息,该怎么办呢?直接把给 ...
- 【转载】MFC动态创建控件及其消息响应函数
原文:http://blog.sina.com.cn/s/blog_4a08244901014ok1.html 这几天专门调研了一下MFC中如何动态创建控件及其消息响应函数. 参考帖子如下: (1)h ...
- ListView中Item与Checkable子类控件抢焦点问题
Android开发中,经常需要为ListView定制Adapter,绑定各种子类控件.如果Item包含Button等Checkable的控件,那么就会发生点击Item无法响应的问题.原因是自己定义的I ...
随机推荐
- linux下新建(mkdir)、删除(rmdir)文件夹
mkdir: 该命令:mkdir ./folder2/folder3 ./当前文件下下一级目录 rmdir:移除文件夹
- ubuntu18.04 LTS解决/boot空间不足
1. 查看磁盘占用情况 df -h Filesystem Size Used Avail Use% Mounted on/dev/nvme0n1p5 181M 141M 27M ...
- cocos源码分析--Sprite绘图原理
精灵是2D游戏中最重要的元素,可以用来构成游戏中的元素,如人物,建筑等,用Sprite类表示,他将一张纹理的一部分或者全部矩形区域绘制到屏幕上.我们可以使用精灵表来减少OpenGL ES 绘制的次数, ...
- 基于centos7的真实机环境下安装 vmware workstastion
通常我们在在虚拟机里面搭建大数据集群,如果我们换在真实机里面搭建大数据集群的话, 我们的每一台电脑就是centos系统了,这个时候如果我们需要按vmware 软件的话我们就需要下载不同的版本了 废话不 ...
- Vsftp的PASV mode(被动模式传送)和Port模式及 Linux下VsFTP配置全方案
什么叫做PASV mode(被动模式传送)?他是如何工作的? FTP的连接一般是有两个连接的,一个是客户程和服务器传输命令的,另一个是数据传送的连接.FTP服务程序一般会支持两种不同的模式,一种是Po ...
- Device supprts x86,armeabi-v7a,but APK only aupports armeabi;模拟机不能运行。
在真机可以运行,模拟机却不可以: 这个是模拟机: 修改: defaultConfig { ndk{ abiFilters "armeabi" } } 为: defaultConfi ...
- angularjs的cache
首先要引入angular-cookies.js插件 angular.module('app').service('cache', ['$cookies', function($cookies){ th ...
- Mac下如何用SSH连接远程Linux服务器,centos无法复制粘贴
CentOS 7.1安装完之后默认已经启动了ssh服务我们可以通过以下命令来查看ssh服务是否启动. 3.2查看22端口是否开放 #netstat -tnl 3.3查看ssh服务是否启动 #syste ...
- 剑指Offer(三):从尾到头打印链表
说明: 1.本系列是根据<剑指Offer>这个系列做的一个小笔记. 2.直接动力是因为师兄师姐找工作很难,而且机械出生的我面试算法更难. 3.刚开始准备刷LeetCode.LintCode ...
- js中script的上下放置区别 , Dom的增删改创建
回顾 javascript分为三部分: 1.ECMAScript5.0 es6(阮一峰) es7 es8 es6中有类的概念 声明变量 var let(es6中语法) 内置函数 Date Math.r ...