首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 訊息過濾機制實作:使用 IMessageFilter 攔截滑鼠左鍵點擊

     編輯:關於C語言

什麼是訊息過濾?

Windows Forms 應用程式建立在訊息迴圈之上,所有輸入(滑鼠、鍵盤)、繪製、計時器等事件皆透過視窗訊息傳遞。IMessageFilter 介面允許在訊息分派到控制項之前全域攔截、檢查、修改或阻止特定訊息,實現熱鍵、全域快捷鍵、輸入驗證、UI 自動化測試等進階功能。

核心介面:IMessageFilter

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_KEYDOWN0x0100按鍵按下
WM_KEYUP0x0101按鍵釋放
WM_CHAR0x0102字元輸入
WM_MOUSEMOVE0x0200滑鼠移動
WM_LBUTTONDOWN0x0201左鍵按下
WM_LBUTTONUP0x0202左鍵釋放
WM_RBUTTONDOWN0x0204右鍵按下
WM_MOUSEWHEEL0x020A滾輪滾動
WM_SETFOCUS0x0007獲得焦點
WM_KILLFOCUS0x0008失去焦點

進階應用場景

1. 全域熱鍵註冊(搭配 RegisterHotKey)

[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);
}

2. 輸入驗證:限制 TextBox 僅接受數字

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;
    }
}

3. UI 自動化測試:記錄使用者操作序列

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/O
  • 範圍:`Application.AddMessageFilter` 影響整個應用程式所有視窗,非單一 Form
  • 順序:多個過濾器按加入順序呼叫,任一回傳 `true` 即停止分派
  • 清理:表單關閉、應用程式結束時務必呼叫 `RemoveMessageFilter`,防止記憶體洩漏或影響後續視窗
  • WPF 不適用:WPF 使用不同的輸入系統,需用 `InputManager` 或 `EventManager` 處理

總結

IMessageFilter 是 Windows Forms 開發者的利器,提供訊息管線層級的攔截能力。善用它可實現全域熱鍵、輸入限制、操作記錄、自動化測試等進階功能。記住:精簡邏輯、明確範圍、及時清理,即能安全發揮其威力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved