揭开.NET消息循环的神秘面纱(二)(转)

本文转自:http://www.cnblogs.com/sakiwer/archive/2009/12/15/1619281.html

 

-----------------------------------------------------------------------------------------------------------------------------------------------------

 

何谓消息

  鼠标移动、按键被按下、窗口被关闭.,这些都会产生消息。在Windows操作系统中,消息是以下面的数据结构存在的(定义在WinUser.h档案中):..

typedef struct tagMSG { 
HWND hwnd; 
UINT message; 
WPARAM wParam; 
LPARAM lParam; 
DWORD time; 
POINT pt; 
} MSG;

消息内有六个信息,分别是:

. hwnd:窗口/控件的唯一hwnd的编号。消息循环会根据此信息,将消息送到正确目标。

. message:Windows预先定义的消息种类的ID。

. wParam 与lParam:有些message本身需要携带更多的信息,这些信息就放在wParam与lParam中。

. time与pt:消息发生当时的时间与鼠标位置。

.NET Framework如何封装消息循环

.NET Framework的Windows Forms将消息循环封装起来,以方便我们使用。本节中所提到的类(class),都是属于System.Windows.Forms名字空间(namespace)。

简单归纳如下:消息循环被封装进了Application类的Run()静态方法中;Windows Procedure被封装进了NativeWindow 与Control 类中;个别的消息处理动作被封装进Control 类的OnXyz()(例如OnPaint())。我们可以覆盖(override)OnXyz(),来提供我们自己的程序。也可以利用.NET的事件 (event)机制,在Xyz事件上,加入我们的事件处理函数(Event Handler)。Control类的OnXyz()会主动调用Xyz 事件的处理函数。

请注意,因为Xyz 的事件处理函数是由Control类的OnXyz()方法所调用的,所以当你覆写OnXyz()方法时,不要忘了调用Control类的OnXyz() (除非你有特殊需求),否则Xyz事件处理函数将会没有作用。只要调用base.OnXyz(),就可以调用到Control类的OnXyz()方法,如 下所示:

protected override void OnPaint(PaintEventArgs e){ 
base.OnPaint (e);// TODO: 加入 Form1.OnPaint 实作
}

我们可以利用覆写Control类的OnXyz(),来决定该消息发生时要做些什么。同理,我们甚至可以覆写Control与NativeWindow类的WndProc(),来定义Windows Procedure。

再次提醒你,因为OnXyz()系列方法是由Control类的WndProc()所调用的,所以当你覆写WndProc()时,不要忘了调用 Control类的WndProc()(除非你有特殊需求),否则OnXyz()系列方法(以及Xyz事件处理函数)将会没有作用。只要调用 base.WndProc(),就可以调用到Control类的WndProc(),如下所示:

protected override void WndProc(ref Message m){ 
base.WndProc (ref m);//TODO: 加入Form1.WndProc 实作
}

你可能也注意到了,WndProc()需要一个Message类的参数,这正是MSG被封装成.NET版本的结果。

一个Windows Forms的范例

为了让读者更加了解实际的状况,我用下面的实例范例作说明:

namespace WindowsApplication1{ 
/// Form1 的摘要描述。
public class Form1 : Form{ 
/// 设计工具所需的变数。
private Container components = null; 
public Form1(){ 
AutoScaleBaseSize = new Size(5, 15); 
ClientSize = new Size(292, 266); 
Name = "Form1"; 
Text = "Form1"; 
Paint += new PaintEventHandler(this.Form1_Paint); 
Paint += new PaintEventHandler(this.Form1_Paint2); 

/// 应用程序的主进入点。
[STAThread] 
static void Main(){ 
Application.Run(new Form1()); 

protected override void OnPaint(PaintEventArgs e){ 
base.OnPaint (e); // 2 

private void Form1_Paint(object sender, PaintEventArgs e){ 
// 3 

private void Form1_Paint2(object sender, PaintEventArgs e){ 
// 4 

protected override void WndProc(ref Message m){ 
base.WndProc (ref m); // 1 


}

1、在Main()中,利用Application.Run()来将Form1窗口显示出来,并进入消息循环。程序的执行过程中,Application.Run()一直未结束。

2、OS在此Process的消息队列内放进一个WM_PAINT消息,好让窗口被显示出来。

3、WM_PAINT被Application.Run()内的消息循环取出来,分发到WndProc()。由于多态(Polymorphism)的因 素,此次调用(invoke)到的WndProc()是属于Form1的WndProc(),也就是上述程序中批注(comment)1的地方,而不是调 用到 Control.WndProc()。

4、在Form1.WndProc()的最后,有调用base.WndProc(),这实际上调用到Control.WndProc()。

5、Control.WndProc()从Message参数中得知此消息是WM_PAINT,于是调用OnPaint()。由于多态的因素,此次调用 到的OnPaint()是属于Form1的OnPaint(),也就是上述程序中批注2的地方,而不是调用到 Control.OnPaint()。

6、在Form1.OnPaint()的最后,有调用base.OnPaint(),这实际上调用到Control.OnPaint()。

7、我们曾经在Form1的构造函数(constructor)中将Form1_Paint()与Form1_Paint2()登记成为Paint事件处理函数
(Event Handler)。Control.OnPaint()会去依序去调用这两个函数,也就是上述程序中批注3与4的地方。

干嘛知道这么多?拜工具之赐,现在的程序员很幸福,可以在糊里胡涂的情况下写出程序来。不过这样的程序员恐怕竞争力不强,毕竟将组件 (component)拖放(drag and drop)到画面上,再设定组件属性的工作,称不上有太大的难度。只有深入了解内部原理,才能让自己对技术融会贯通,也才能让程序员之路走得更稳健、更长 久。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值