Windows Forms 應用程式建立在訊息迴圈之上,所有輸入(滑鼠、鍵盤)、繪製、計時器等事件皆透過視窗訊息傳遞。IMessageFilter 介面允許在訊息分派到控制項之前全域攔截、檢查、修改或阻止特定訊息,實現熱鍵、全域快捷鍵、輸入驗證、UI 自動化測試等進階功能。
public interface IMessageFilter
{
///
/// 攔截訊息
///
/// 訊息結構(傳址,可修改)
/// true = 過濾掉(不分派);false = 繼續分派
bool PreFilterMessage(ref Message m);
}
using System;
using System.Windows.Forms;
///
/// 攔截 WM_LBUTTONDOWN(0x201)、WM_LBUTTONUP(0x202)、WM_LBUTTONDBLCLK(0x203)
///
public class LeftClickBlocker : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_LBUTTONDBLCLK = 0x0203;
public bool PreFilterMessage(ref Message m)
{
// 過濾滑鼠左鍵相關訊息
if (m.Msg >= WM_LBUTTONDOWN && m.Msg <= WM_LBUTTONDBLCLK)
{
Console.WriteLine($"攔截滑鼠左鍵訊息: 0x{m.Msg:X} (HWND: 0x{m.HWnd.ToInt64():X})");
return true; // 過濾掉,不分派給控制項
}
return false; // 其他訊息正常處理
}
}
///
/// 主視窗:示範加入/移除訊息過濾器
///
public class MainForm : Form
{
private readonly LeftClickBlocker _filter = new LeftClickBlocker();
private readonly Button _toggleButton;
public MainForm()
{
Text = "訊息過濾器示範";
Size = new System.Drawing.Size(400, 300);
_toggleButton = new Button
{
Text = "啟用滑鼠左鍵封鎖",
Dock = DockStyle.Top,
Height = 40
};
_toggleButton.Click += ToggleFilter;
Controls.Add(_toggleButton);
// 顯示狀態標籤
var statusLabel = new Label
{
Text = "嘗試點擊按鈕或表單任意處...",
Dock = DockStyle.Bottom,
Height = 30,
TextAlign = ContentAlignment.MiddleCenter
};
Controls.Add(statusLabel);
}
private void ToggleFilter(object sender, EventArgs e)
{
if (Application.MessageFilters.Contains(_filter))
{
Application.RemoveMessageFilter(_filter);
_toggleButton.Text = "啟用滑鼠左鍵封鎖";
}
else
{
Application.AddMessageFilter(_filter);
_toggleButton.Text = "停用滑鼠左鍵封鎖";
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
// 確保表單關閉時移除過濾器,避免影響其他視窗
Application.RemoveMessageFilter(_filter);
base.OnFormClosing(e);
}
}
// 程式進入點
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
| 常數 | 十六進位 | 說明 |
|---|---|---|
| WM_KEYDOWN | 0x0100 | 按鍵按下 |
| WM_KEYUP | 0x0101 | 按鍵釋放 |
| WM_CHAR | 0x0102 | 字元輸入 |
| WM_MOUSEMOVE | 0x0200 | 滑鼠移動 |
| WM_LBUTTONDOWN | 0x0201 | 左鍵按下 |
| WM_LBUTTONUP | 0x0202 | 左鍵釋放 |
| WM_RBUTTONDOWN | 0x0204 | 右鍵按下 |
| WM_MOUSEWHEEL | 0x020A | 滾輪滾動 |
| WM_SETFOCUS | 0x0007 | 獲得焦點 |
| WM_KILLFOCUS | 0x0008 | 失去焦點 |
[DllImport("user32.dll")]
static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
static extern bool UnregisterHotKey(IntPtr hWnd, int id);
public class HotKeyFilter : IMessageFilter
{
private const int WM_HOTKEY = 0x0312;
private readonly IntPtr _hWnd;
public HotKeyFilter(IntPtr hWnd) => _hWnd = hWnd;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_HOTKEY && m.HWnd == _hWnd)
{
int id = m.WParam.ToInt32();
OnHotKeyPressed(id);
return true;
}
return false;
}
public event Action HotKeyPressed;
protected virtual void OnHotKeyPressed(int id) => HotKeyPressed?.Invoke(id);
}
public class NumericInputFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x0102) // WM_CHAR
{
char c = (char)m.WParam.ToInt32();
if (!char.IsDigit(c) && c != '\b') // 允許退格鍵
{
return true; // 過濾非數字字元
}
}
return false;
}
}
public class UITraceFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
// 記錄關鍵訊息供測試回放
if (m.Msg == 0x0201 || m.Msg == 0x0100) // 點擊或按鍵
{
Trace.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] Msg=0x{m.Msg:X}, HWND=0x{m.HWnd.ToInt64():X}, WParam=0x{m.WParam.ToInt64():X}");
}
return false;
}
}
PreFilterMessage 會在每個訊息呼叫,邏輯務必精簡,避免複雜運算或 I/OIMessageFilter 是 Windows Forms 開發者的利器,提供訊息管線層級的攔截能力。善用它可實現全域熱鍵、輸入限制、操作記錄、自動化測試等進階功能。記住:精簡邏輯、明確範圍、及時清理,即能安全發揮其威力。