在 C# WinForms 開發中,攔截並處理底層 Windows 訊息(如滑鼠點擊、鍵盤按下、視窗移動等)是實現自訂行為、全域熱鍵、拖放攔截等進階功能的基礎。本文介紹兩種主流方法:覆寫 DefWndProc 與實作 IMessageFilter,並附上常用訊息 ID 速查表。
每個 Form 都有虛擬方法 DefWndProc(ref Message m),所有未被特定事件處理的訊息最終都會流經此處。覆寫它即可攔截該視窗的所有訊息。
protected override void DefWndProc(ref Message m)
{
// WM_LBUTTONDOWN = 0x0201
if (m.Msg == 0x0201)
{
MessageBox.Show($"攔截到左鍵按下: Msg=0x{m.Msg:X}");
// 若不呼叫 base.DefWndProc,訊息將被「吃掉」,視窗不會收到點擊
return;
}
base.DefWndProc(ref m); // 其餘訊息交給預設處理
}
優點:簡單直接,僅影響當前視窗。
缺點:無法攔截其他視窗或應用程式層級的訊息。
實作 System.Windows.Forms.IMessageFilter 介面,並透過 Application.AddMessageFilter 註冊,可攔截整個應用程式所有視窗的訊息。
public class MessageFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
// WM_LBUTTONDOWN = 0x0201
if (m.Msg == 0x0201)
{
MessageBox.Show("全域攔截: WM_LBUTTONDOWN");
return true; // true = 過濾掉(不再分發),false = 繼續分發
}
return false;
}
}
// 在 Program.cs Main() 中註冊:
static class Program
{
private static MessageFilter _msgFilter = new MessageFilter();
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.AddMessageFilter(_msgFilter); // 註冊
Application.Run(new Form1());
// 結束前可移除:Application.RemoveMessageFilter(_msgFilter);
}
}
優點:全域攔截,適合熱鍵、全域滑鼠手勢、記錄器。
缺點:影響範圍大,需謹慎判斷 m.HWnd 區分視窗,避免誤攔截導致 UI 假死。
| 常數名稱 | Hex 值 | 說明 |
|---|---|---|
| WM_LBUTTONDOWN | 0x0201 | 滑鼠左鍵按下 |
| WM_LBUTTONUP | 0x0202 | 滑鼠左鍵放開 |
| WM_RBUTTONDOWN | 0x0204 | 滑鼠右鍵按下 |
| WM_MOUSEMOVE | 0x0200 | 滑鼠移動 |
| WM_KEYDOWN | 0x0100 | 鍵盤按下 |
| WM_KEYUP | 0x0101 | 鍵盤放開 |
| WM_SYSKEYDOWN | 0x0104 | 系統鍵(Alt)按下 |
| WM_PAINT | 0x000F | 重繪請求 |
| WM_CLOSE | 0x0010 | 關閉視窗 |
| WM_DESTROY | 0x0002 | 視窗銷毀 |
| WM_MOVE | 0x0003 | 視窗移動 |
| WM_SIZE | 0x0005 | 視窗大小改變 |
| WM_TIMER | 0x0113 | 計時器觸發 |
| WM_COMMAND | 0x0111 | 命令(選單、按鈕) |
| WM_NOTIFY | 0x004E | 通用控件通知 |
| WM_USER | 0x0400 | 自訂訊息起始值 |
SetWindowsHookEx (WH_GETMESSAGE/WH_CALLWNDPROC) 並配合 DLL 注入,屬更進階主題。掌握訊息攔截,能讓你在 WinForms 中實現許多標準事件無法覆蓋的深度客製化行為。