一、C#消息概述
Windows下應用程序的執行是通過(guò)消息驅動(dòng)的。消息是整個(gè)應用程序的工作引擎,我們需要理解掌握我們使用的編程語(yǔ)言是如何封裝消息的原理。
1 什么是消息(Message)
消息就是通知和命令。在.NET框架類(lèi)庫中的System.Windows.Forms命名空間中微軟采用面對對象的方式重新定義了Message。新的消息(Message)結構的公共部分屬性基本與早期的一樣,不過(guò)它是面對對象的。
公共屬性:
HWnd 獲取或設定消息的處理函數
Msg 獲取或設定消息的ID號
Lparam 指定消息的LParam字段
Wparam 指定消息的WParam字段
Result 指定為響應消息處理函數而向OS系統返回的值
2 消息驅動(dòng)的過(guò)程
所有的外部事件,如鍵盤(pán)輸入、鼠標移動(dòng)、按動(dòng)鼠標都由OS系統轉換成相應的消息發(fā)送到應用程序的消息隊列。每個(gè)應用程序都有一段相應的程序代碼來(lái)檢索、分發(fā)這些消息到對應的窗體,然后由窗體的處理函數來(lái)處理。
二、C#消息的封裝
C#對消息重新進(jìn)行了面對對象的封裝,在C#中消息被封裝成了事件。
System.Windows.Forms.Application類(lèi)具有用于啟動(dòng)和停止應用程序和線(xiàn)程以及處理Windows消息的方法。
調用Run以啟動(dòng)當前線(xiàn)程上的應用程序消息循環(huán),并可以選擇使其窗體可見(jiàn)。
調用Exit或ExitThread來(lái)停止消息循環(huán)。
C#中用Application類(lèi)來(lái)處理消息的接收和發(fā)送的。消息的循環(huán)是由它負責的。
從本質(zhì)上來(lái)講,每個(gè)窗體一般都對應一個(gè)窗體過(guò)程處理函數。那么,C#的一個(gè)Form實(shí)例(相當于一個(gè)窗體)收到消息后是如何處理消息的?其實(shí),這個(gè)問(wèn)題的分析也就是展示了C#的消息封裝原理。
實(shí)現鼠標左鍵按下的消息的響應(WM_LBUTTONDOWN)
- this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown1);
- this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown2);
- private void Form1_MouseDown1(object sender, System.Windows.Forms.MouseEventArgs e)
- {
- if(e.Button==System.Windows.Forms.MouseButtons.Left)
- System.Windows.Forms.MessageBox.Show("消息被Form1_MouseDown1函數響應");
- }
- private void Form1_MouseDown2(object sender, System.Windows.Forms.MouseEventArgs e)
- {
- if(e.Button==System.Windows.Forms.MouseButtons.Left)
- System.Windows.Forms.MessageBox.Show("消息被Form1_MouseDown2函數響應");
- }
上面this.MouseDown是C#中的一個(gè)事件。它的定義如下:
- public event MouseEventHandler MouseDown;
而MouseEventHandler的定義為:
- public delegate void MouseEventHandler( object sender,MouseEventArgs e);
實(shí)際上,上面定義了一個(gè)委托類(lèi)型MouseEventHandler。委托了啟用了其它編程語(yǔ)言中的函數指針的解決方案。與C++的函數指針不同,委托是完全面向對象的,同時(shí)封裝了對象實(shí)例和方法。本質(zhì)上,委托把一個(gè)實(shí)例和該實(shí)例上的方法函數封裝成一個(gè)可調用的實(shí)體,它是面對對象的、安全的。
我們可以把
- this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown1);
這條語(yǔ)句看成向this.MouseDown添加一個(gè)函數指針。
事件是對象發(fā)送的消息,以發(fā)送信號通知操作的發(fā)生。引發(fā)(觸發(fā))事件的對象叫做事件發(fā)送方。捕獲事件并對事件作出響應的對象叫做事件接收方。在事件通訊中,事件發(fā)送方類(lèi)并不知道哪個(gè)對象或方法將接收到(處理)它引發(fā)的事件。所需要的是在發(fā)送方和接收方之間存在一個(gè)媒介(類(lèi)似指針的機制)。.NET框架定義了一個(gè)特殊的類(lèi)型(Delegate委托),該類(lèi)型提供函數指針的功能。這樣,委托就等效于一個(gè)類(lèi)型安全的函數指針或一個(gè)回調函數。
前面我們向this.MouseDown事件添加了兩個(gè)委托。
- this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown1);
- this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown2);
結果,我們的兩個(gè)函數Form1_MouseDown1、Form1_MouseDown2在我們單擊鼠標左鍵的時(shí)候都會(huì )被調用,而且調用的順序和我們添加委托的順序一致。
WM_LBUTTONDOWN消息首先被Application類(lèi)從應用程序消息隊列中取出,然后分發(fā)到相應的窗體。窗體使用MouseDown事件中的函數指針調用已經(jīng)添加的響應函數。所以C#中的事件字段實(shí)質(zhì)上是一個(gè)函數指針列表,用來(lái)維護一些消息到達時(shí)的響應函數的地址。
三、結論
C#消息的工作流程:
C#消息被Application類(lèi)從應用程序消息隊列中取出,然后分發(fā)到消息對應的窗體,窗體對象的第一個(gè)響應函數是對象中的protected override void WndProc(ref System.Windows.Forms.Message e)方法。
它再根據消息的類(lèi)型調用默認的消息響應函數(如OnMouseDown),默認的響應函數然后根據對象的事件字段(如this.MouseDown )中的函數指針列表,調用用戶(hù)所加入的響應函數(如Form1_MouseDown1和Form1_MouseDown2),而且調用順序和用戶(hù)添加順序一致。
四、再回首Application類(lèi)
Application類(lèi)有一個(gè)AddMessageFilter的靜態(tài)方法,通過(guò)它我們可以添加消息篩選器,以便在向目標傳遞Windows消息時(shí),檢視這些消息。
使用消息篩選器來(lái)防止引發(fā)特定事件,或在將某事件傳遞給事件處理程序之前使用消息篩選器對其執行特殊操作。我們必須提供IMessageFilter接口的一個(gè)實(shí)現,然后才可以使用消息篩選器。以下的示范代碼將演示在消息發(fā)往窗體前我們如何攔截它。我們攔截的同樣是WM_LBUTTONDOWN消息。
- using System;
- using System.Drawing;
- using System.Collections;
- using System.ComponentModel;
- using System.Windows.Forms;
- using System.Data;
- namespace MessageMech3
- {
- //實(shí)現消息過(guò)濾器接口
- public class CLButtonDownFilter : IMessageFilter
- {
- public bool PreFilterMessage(ref Message m)
- {
- if (m.Msg==0x0201)// WM_LBUTTONDOWN
- {
- System.Windows.Forms.MessageBox.Show("App中鼠標左鍵按下");
- //返回值為true, 表示消息已被處理,不要再往后傳遞,因此消息被截獲
- //返回值為false,表示消息未被處理,需要再往后傳遞,因此消息未被截獲
- return true;
- }
- return false;
- }
- }
- /// < summary>
- /// Summary description for WinForm.
- /// < /summary>
- public class WinForm : System.Windows.Forms.Form
- {
- /// < summary>
- /// Required designer variable.
- /// < /summary>
- private System.Windows.Forms.Label label1;
- private System.ComponentModel.Container components = null;
- public WinForm()
- {
- //
- // Required for Windows Form Designer support
- //
- InitializeComponent();
- //
- // TODO: Add any constructor code after InitializeComponent call
- //
- //安裝自己的過(guò)濾器
- CLButtonDownFilter MyFilter=new CLButtonDownFilter();
- System.Windows.Forms.Application.AddMessageFilter(MyFilter);
- }
- /// < summary>
- /// Clean up any resources being used.
- /// < /summary>
- protected override void Dispose (bool disposing)
- {
- if (disposing)
- {
- if (components != null)
- {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
- #region Windows Form Designer generated code
- /// < summary>
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- /// < /summary>
- private void InitializeComponent()
- {
- this.label1 = new System.Windows.Forms.Label();
- this.SuspendLayout();
- //
- // label1
- //
- this.label1.BackColor = System.Drawing.Color.Transparent;
- this.label1.Dock = System.Windows.Forms.DockStyle.Top;
- this.label1.ForeColor = System.Drawing.Color.DarkViolet;
- this.label1.Name = "label1";
- this.label1.Size = new System.Drawing.Size(440, 32);
- this.label1.TabIndex = 0;
- this.label1.Text = "演示如何在A(yíng)pp對象中處理消息,請點(diǎn)鼠標左鍵";
- this.label1.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
- //
- // Form1
- //
- this.AutoScaleBaseSize = new System.Drawing.Size(7, 22);
- this.BackColor = System.Drawing.Color.WhiteSmoke;
- this.ClientSize = new System.Drawing.Size(440, 273);
- this.Controls.AddRange(new System.Windows.Forms.Control[] {this.label1});
- this.Font = new System.Drawing.Font("華文行楷", 15F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(134)));
- this.Name = "WinForm";
- this.Text = "WinForm";
- //消息響應函數的調用順序和添加委托的順序一致
- //即:以下命令將先調用Form1_MouseDown1再調用Form1_MouseDown2
- //通過(guò)委托添加自己的鼠標按鍵消息響應函數1
- this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown1);
- //通過(guò)委托添加自己的鼠標按鍵消息響應函數2
- this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown2);
- this.ResumeLayout(false);
- }
- #endregion
- /// < summary>
- /// 應用程序的主入口點(diǎn)。
- /// < /summary>
- [STAThread]
- static void Main()
- {
- Application.Run(new WinForm()); //啟動(dòng)當前Form線(xiàn)程上的應用程序消息循環(huán)
- }
- //要點(diǎn)1
- // 通過(guò)C#提供的事件接口添加自己的鼠標按鍵事件的響應函數
- //
- private void Form1_MouseDown1(object sender, System.Windows.Forms.MouseEventArgs e)
- {
- if(e.Button==System.Windows.Forms.MouseButtons.Left)
- System.Windows.Forms.MessageBox.Show("消息被Form1_MouseDown1函數響應");
- }
- private void Form1_MouseDown2(object sender, System.Windows.Forms.MouseEventArgs e)
- {
- if(e.Button==System.Windows.Forms.MouseButtons.Left)
- System.Windows.Forms.MessageBox.Show("消息被Form1_MouseDown2函數響應");
- }
- //要點(diǎn)2
- //通過(guò)覆蓋基類(lèi)的事件引發(fā)函數攔截消息
- //
- protected override void OnMouseDown( MouseEventArgs e)
- {
- if(e.Button==System.Windows.Forms.MouseButtons.Left)
- System.Windows.Forms.MessageBox.Show("消息被OnMouseDown函數響應");
- //如果需要截獲消息,可將base.OnMouseDown(e);語(yǔ)句注釋掉
- base.OnMouseDown(e);
- }
- //要點(diǎn)3
- //通過(guò)覆蓋基類(lèi)的窗體函數攔截消息
- //
- protected override void WndProc(ref System.Windows.Forms.Message e)
- {
- //如果需要截獲消息,
- //if(e.Msg==0x0201)// WM_LBUTTONDOWN
- // System.Windows.Forms.MessageBox.Show("消息被WndProc函數響應");
- //else
- // base.WndProc(ref e);
- //不需要截獲消息則為
- if(e.Msg==0x0201)// WM_LBUTTONDOWN
- System.Windows.Forms.MessageBox.Show("消息被WndProc函數響應");
- base.WndProc(ref e);
- }
- }
- }
以上代碼我們首先用類(lèi)CLButtonDownFilter實(shí)現了IMessageFilter接口,在WinForm初始化的時(shí)候我們安裝了消息篩選器。程序實(shí)際執行的時(shí)候,在點(diǎn)擊鼠標左鍵的時(shí)候,程序僅僅會(huì )彈出一個(gè)"App中鼠標左鍵按下"的消息框。因為我們在消息發(fā)往窗體前攔截了它,所以窗體將接收不到WM_LBUTTONDOWN消息。
如果我們把
- if (m.Msg==0x0201)// WM_LBUTTONDOWN
- {
- System.Windows.Forms.MessageBox.Show("App中鼠標左鍵按下");
- return true;
- }
改成
- if (m.Msg==0x0201)// WM_LBUTTONDOWN
- {
- System.Windows.Forms.MessageBox.Show("App中鼠標左鍵按下");
- return false;
- }
那么,我們在A(yíng)pplication類(lèi)處理消息后,消息將繼續發(fā)往窗體。窗體的函數將可以處理此消息。程序執行效果是順序彈出5個(gè)消息框。
1:< < App中鼠標左鍵按下>>
2:< < 消息被WndProc函數響應>>
3:< < 消息被OnMouseDown函數響應>>
4:< < 消息被Form1_MouseDown1函數響應>>
5:< < 消息被Form1_MouseDown2函數響應>>
主要有兩種方法過(guò)濾實(shí)現過(guò)濾
第一種:
- protected override void WndProc(ref Message m)
- {
- if (m.Msg == 0x0201)
- return;
- else
- base.WndProc(ref m);
- }
第二種
不重寫(xiě)WndProc
- //實(shí)現消息過(guò)濾器接口
- public class CLButtonDownFilter : IMessageFilter
- {
- public bool PreFilterMessage(ref Message m)
- {
- if (m.Msg == 0x0201)// WM_LBUTTONDOWN
- {
- //返回值為true, 表示消息已被處理,不要再往后傳遞,因此消息被截獲
- //返回值為false,表示消息未被處理,需要再往后傳遞,因此消息未被截獲
- return true;
- }
- return false;
- }
- }
CLButtonDownFilter MyFilter = new CLButtonDownFilter();
System.Windows.Forms.Application.AddMessageFilter(MyFilter);
關(guān)于C#消息的相關(guān)知識就介紹到這里。
聯(lián)系客服