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;

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

时间: 2024-10-21 16:11:21

Delphi的子类化控件消息, 消息子类化的相关文章

眼见为实(2):介绍Windows的窗口、消息、子类化和超类化

眼见为实(2):介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较"生僻"的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和线程.子类化(Subclassing)和超类化(Superclassing)是伴随Windows窗口机制而产生的两个复用代码的方法.不要把"子类化.超类化"与面向对象语言中的派生类.基类混淆起来."子类化.超类化"中的"类"是指W

子类化和超类化区别(介绍Windows的窗口、消息、子类化和超类化)(转)

原文地址:http://maqianli210.blog.sohu.com/75497589.html 这篇文章本来只是想介绍一下子类化和超类化这两个比较“生僻”的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和线程.子类化(Subclassing)和超类化(Superclassing)是伴随Windows窗口机制而产生的两个复用代码的方法.不要把“子类化.超类化”与面向对象语言中的派生类.基类混淆起来.“子类化.超类化”中的“类”是指Windows的窗口类. 0 运

【转载】MFC动态创建控件及其消息响应函数

原文:http://blog.sina.com.cn/s/blog_4a08244901014ok1.html 这几天专门调研了一下MFC中如何动态创建控件及其消息响应函数. 参考帖子如下: (1)http://topic.csdn.net/u/20101204/13/5f1b1e70-2f1c-4205-ba10-41616d16f07e.html (2)http://www.cppblog.com/deercoder/articles/107132.html 这两篇帖子是比较经典的,能够回答

如何给对话框中的控件发送消息呢?Windows消息分类

以博文CTabCtrl中介绍的那样,给Tab添加子对话框来显示Tab内容.那么如果这个子对话框中含有个CTreeCtrl控件,有个Button控件,我想要模拟给这两个控件发送消息,该怎么办呢?直接把给控件的消息给控件容器(控件的父窗口)是没有用的.为什么呢?首先要明白windows的消息分类: Windows消息的分类 1. 标准消息(队列消息)除WM_COMMAND之外,所有以WM_开头的消息都是标准消息,如WM_MOUSEMOVE.WM_LBUTTONUP.WM_KEYDOWN.WM_C

[转]windows控件消息和控件通知消息大全

本篇文章主要介绍了"windows控件消息和控件通知消息大全",主要涉及到windows控件消息和控件通知消息大全方面的内容,对于windows控件消息和控件通知消息大全感兴趣的同学可以参考一下. Edit Control Notification Codes EN_SETFOCUS EN_KILLFOCUS EN_CHANGE EN_UPDATE EN_ERRSPACE EN_MAXTEXT EN_HSCROLL EN_VSCROLL Edit Control Messages EM

Delphi 编写ActiveX控件(OCX控件)的知识和样例(有详细步骤)

一.ActiveX应用情况简介: ActiveX控件也就是一般所说的OCX控件,它是 ActiveX技术的一部分.ActiveX是微软公司推出的基于组件对象模型COM的技术,包括对Windows 32位应用编程接口(Win32 API)和组件对象模型的一系列扩充和增强,目标是把计算机桌面环境与因特网环境集成起来,同时保护在Windows技术中现有的开发投资.微软的 ActiveX技术根本上就是修改过的OCX技术,使它能够跨越Internet,主要是使用WWW来传递控件.            A

(一)Android使用自定义注解来初始化控件

一般情况下我们开发Android应用,初始化控件使用findViewById(),可是一个项目开发完毕,你会发现很多这样的代码,其实是重复的.这个时候你就会发现Java自带的注释(Annotation)是多么的方便了. 一.设计一个ContentView的注释 因为这个注释我们要运在Activity类上,所以我们要申明@Targrt(ElementType.TYPE). [java] view plaincopyprint? @Target(ElementType.TYPE) @Retentio

.NET代码混淆、保护和优化控件Spices .Net Obfuscator

Spices .Net Obfuscator控件用于防止您的.NET软件被黑客.竞争对手或者窥探者进行逆向工程.代码篡改或是未经授权就可使用,该工具可提供.NET代码混淆.保护和优化等,完全保护您的.NET代码和数据安全.Spices.Net Obfuscator 重新构建您的.NET程序集为不可能被拆卸.反编译的新的格式. 具体功能: 支持所有.NET语言c#, VB.Net, managed c++, J# 完全支持MS Visual Studio 2003 / 2005 / 2008 /

zw版【转发·台湾nvp系列Delphi例程】Delphi 使用 HALCON库件COM控件数据格式转换

zw版[转发·台湾nvp系列Delphi例程]Delphi 使用 HALCON库件COM控件数据格式转换 Delphi 使用 HALCON库件COM控件数据格式转换,与IHObjectX接口有关 var rg0,rg1 : HRegionX;begin rg0 := CoHRegionX.Create; rg0.GenRectangle1(10,10,150,150); rg1 := CoHRegionX.Create; rg1.GenRectangle1(200,200,250,250); r