在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
永远记住,无论你是用 SDK 还是借用 VCL 来创建窗口,都要遵循 Windows 的游戏规则,即先注册窗口类,然后再创建窗口实例,在消息循环中写实现代码。你还要知道 Windows 已经为了我们预注册了多个窗口类,例如“Edit”、“ComboBox”,这时候我们要做的就是直接创建这些窗口,无需注册窗口类了;在 Delphi 中这一切更简单了,VCL 全部为你做好了,你只需简单地在设计窗体上拖动你要的控件再写实现代码就可以了,是不是很 cool? 新建一个 Application,在 Form1 上放一个 Button (缺省名为Button1),在其 OnClick 事件中随便写点代码,加上断点,在调试之前,请打开 DCU 调试开关(Project->Options->Compiler->Use Debug DCUs), 这个开关如果不打开,是没法调试 VCL 的,然后 F9 运行,当停留在断点上时,打开Call Stack 窗口(View->Debug Window->Call Stack)可看到调用顺序如下(从底往上看): TForm1.Button1Click(37C0) TControl.Click TButton.Click TButton.CNCommand((48401, 660, 0, 524948, 0)) TControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TWinControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TButtonControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TControl.Perform(48401,660,524948) DoControlMsg(524948,(no value)) TWinControl.WMCommand((273, 660, 0, 524948, 0)) TCustomForm.WMCommand((273, 660, 0, 524948, 0)) TControl.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TWinControl.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TCustomForm.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) TWinControl.MainWndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0)) StdWndProc(918056,273,660,524948) TWinControl.DefaultHandler((no value)) TControl.WMLButtonUp((514, 0, 48, 13, (48, 13), 0)) TControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0)) TWinControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0)) TButtonControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0)) TWinControl.MainWndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0)) StdWndProc(524948,514,0,852016) TApplication.HandleMessage TApplication.Run Project1 一个 Button 被点击,在 TButton 内部会发生两个消息:WM_LBUTTONDOWN/WM_LBUTTONUP, TButton 没有处理 WM_LBUTTONUP(问题:为什么只响应 WM_LBUTTONUP,这两个消息只应该发生在 Windows 原生控件内,除非 TButton subclass 了 "Button",这部分代码我没看),只是交给 TWinControl.DefaultHandler,随后 TButton 又将生成的 WM_COMMAND 消息发送给它的 Parent,即 TForm,经过一系列消息传递, WM_COMMAND 在 TWinControl.WMCommand 中被处理,通过 DoControlMsg 将 WM_COMMAND 加工成 CN_COMMAND,再利用 TControl.Perform 将 CN_COMMAND 传回 TButton,又通过一系列的消息传递到 TButton 中的 Dispatch,通过查询动态方法表找到 Handler -- TButton.CNCommand,它又调用虚方法 TButton.Click,继而调用 TControl.Click,在这个方法中会调用 FOnClick,而 FOnClick 特性值的内容就是当程序员使用对象查看器撰写 TButton 的 OnClick 事件处理函数时 Delphi 便会自动指定给 TButton 的 OnClick 特性,例子中 OnClick 被指定为 TForm1.Button1Click,因此 TForm1.Button1Click 最终被调用。 TForm1.FormMouseDown(???,???,[ssLeft],346,212) TControl.MouseDown(mbLeft,[ssLeft],346,212) TControl.DoMouseDown((513, 1, 346, 212, (346, 212), 0),mbLeft,[]) TControl.WMLButtonDown((513, 1, 346, 212, (346, 212), 0)) TControl.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0)) TWinControl.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0)) TCustomForm.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0)) TWinControl.MainWndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0)) StdWndProc(2687598,513,1,13893978) TApplication.HandleMessage TApplication.Run Project1 鼠标在 Form 上点击,产生两个消息 WM_LBUTTONDOWN/WM_LBUTTONUP,但我们只截获 WM_LBUTTONDOWN。产生的 WM_LBUTTONDOWN 经过一系列的消息传递到达 TObject.Dispatch,通过查询动态方法表在 TForm 的父类 TControl 中找到了 Handler -- TControl.WMLButtonDown,在 TControl.WMLButtonDown 中又经过 TControl.DoMouseDown、TControl.MouseDown 一系列方法调用,最终调用到 FOnMouseDown,FOnMouseDown 被赋值为 TForm1.FormMouseDown,调用 FOnMouseDown 即调用 TForm1.FormMouseDown。 如果你是共享软件作者,经常会为你的软件被 Crack 掉所烦恼,你能做的就是要加强你的软件的 Anti-Crack 功能,今天就交你一招。 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) btnRegister: TButton; btnCancel: TButton; procedure btnCancelClick(Sender: TObject); private procedure btnRegisterClick(Sender: TObject); procedure WMCommand(var Message: TWMCommand); message WM_COMMAND; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.btnCancelClick(Sender: TObject); begin Close; end; procedure TForm1.btnRegisterClick(Sender: TObject); begin ShowMessage('Thx for ur registration.'); end; procedure TForm1.WMCommand(var Message: TWMCommand); begin if Message.NotifyCode = BN_CLICKED then if FindControl(Message.Ctl) = btnRegister then begin btnRegisterClick(Self); Exit; end; inherited; end; end. 这个方法的本质就是截获 TForm1 的 WM_COMMAND 消息并自己处理,请自行分析代码,我就不多说了。编译完后你可以用 DEDE 反汇编一下,看看还能不能那么容易地找到 TForm1.btnRegisterClick 的入口地址。 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论