在 WinForm 中一般采用重寫 WndProc 的方法對(duì)窗口或控件接受到的指定消息進(jìn)行處理
示例:禁止通過關(guān)閉按鈕或其他發(fā)送 WM_CLOSE 消息的途徑關(guān)閉窗口
| protected override void WndProc(ref Message m) |
| { |
| const int WM_CLOSE = 0x0010; |
| if(m.Msg == WM_CLOSE) |
| { |
| |
| return; |
| } |
| base.WndProc(ref m); |
| } |
Control 類中還有個(gè) DefWndProc 為默認(rèn)的窗口過程
WPF HwndSource
WPF 僅本機(jī)窗口或 HwndHost 嵌入控件擁有句柄,可通過 HwndSource 添加消息處理
示例:禁止通過關(guān)閉按鈕或其他發(fā)送 WM_CLOSE 消息的途徑關(guān)閉窗口
| HwndSource source = null; |
|
|
| protected override void OnSourceInitialized(EventArgs e) |
| { |
| base.OnSourceInitialized(e); |
| IntPtr handle = new WindowInteropHelper(this).Handle; |
| source = HwndSource.FromHandle(handle); |
| source.AddHook(WndProc); |
| } |
|
|
| protected override void OnClosed(EventArgs e) |
| { |
| source?.RemoveHook(WndProc); |
| base.OnClosed(e); |
| } |
|
|
| private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) |
| { |
| const int WM_CLOSE = 0x0010; |
| if(msg == WM_CLOSE) |
| { |
| |
| handled = true; |
| } |
| return IntPtr.Zero; |
| } |
? 注意:1.消息過濾器對(duì)于特定線程是唯一的;2.使用消息過濾器可能會(huì)降低程序性能
IMessageFilter 接口允許程序在將消息調(diào)度到控件或窗口之前捕獲消息進(jìn)行預(yù)處理
IMessageFilter 的 PreFilterMessage 與 Control 的 WndProc 接收到的消息是一個(gè)交集關(guān)系,應(yīng)用程序接收到的消息來自系統(tǒng)消息隊(duì)列,相對(duì)來說更全,但會(huì)有部分消息會(huì)直接發(fā)送到窗口或控件而不進(jìn)入系統(tǒng)消息隊(duì)列
實(shí)現(xiàn) IMessageFilter 接口實(shí)例可對(duì)整個(gè)線程消息循環(huán)進(jìn)行預(yù)處理,并根據(jù) m.HWnd 獲取消息傳入的窗口或控件句柄
示例:截獲程序鼠標(biāo)懸浮消息,窗口標(biāo)題顯示當(dāng)前懸浮控件名
| static class Program |
| { |
| [STAThread] |
| static void Main() |
| { |
| Application.EnableVisualStyles(); |
| Application.SetCompatibleTextRenderingDefault(false); |
| var filter = new SampleMsgFilter(); |
| Application.AddMessageFilter(filter); |
| Application.Run(new MainForm()); |
| Application.RemoveMessageFilter(filter); |
| } |
| } |
|
|
| sealed class SampleMsgFilter : IMessageFilter |
| { |
| public bool PreFilterMessage(ref Message m) |
| { |
| const int WM_MOUSEHOVER = 0x02A1; |
| if(m.Msg == WM_MOUSEHOVER && Control.FromHandle(m.HWnd) is Control ctr) |
| { |
| ctr.FindForm().Text = ctr.Name; |
| return true; |
| } |
| return false; |
| } |
| } |
NativeWindow 是 IWin32Window 的低級(jí)封裝,并且和 WinForm Control 一樣擁有 WndProc 和 DefWndProc 方法,故同樣可通過重寫 WndProc 方法處理消息
可以通過 CreateHandle(new CreateParams()) 創(chuàng)建沒有 UI 的僅消息循環(huán)的窗口。比如托盤圖標(biāo)類 NotifyIcon 內(nèi)部會(huì)創(chuàng)建一個(gè) NativeWindow 用來接收任務(wù)欄創(chuàng)建消息 WM_TASKBARCREATED ("TaskbarCreated"),在資源管理器崩潰重啟后重新創(chuàng)建圖標(biāo)。
附加到其他窗口
由于 WinForm Control WndProc 是密封的,處理消息時(shí)必須繼承類型并重寫,需要單獨(dú)進(jìn)行消息處理的窗口或控件較多時(shí),對(duì)原代碼具有很大的侵入性;而 IMessageFilter 是針對(duì)整個(gè)應(yīng)用程序的消息循環(huán),官方文檔說使用消息過濾器很可能會(huì)降低程序性能;相對(duì)來說,由于 HwndSource AddHook 和 RemoveHook 不是密封的,WPF 程序可以在不侵入原代碼的條件下處理窗口消息,在可復(fù)用性上面反而還具有優(yōu)勢(shì)。但如果仔細(xì)看看 NativeWindow 源代碼,會(huì)發(fā)現(xiàn)它內(nèi)部調(diào)用了 SetWindowLong GWL_WNDPROC (窗口子類化),可以通過 AssignHandle 附加到任意窗口或控件進(jìn)行消息處理,這個(gè)窗口不限制類型,甚至可以附加到其他程序窗口。
這里提供一個(gè)靜態(tài)輔助類,借助 NativeWindow 簡(jiǎn)化附加窗口消息過程處理操作
| using System; |
| using System.Collections.Generic; |
| using System.Windows.Forms; |
|
|
| namespace Wondershare.WinTool.Helpers |
| { |
| public delegate bool HookProc(ref Message m); |
|
|
| public static class MessageHooker |
| { |
| sealed class HookWindow : NativeWindow |
| { |
| List<KeyValuePair<HookProc, Action>> hooks; |
|
|
| public HookWindow(IntPtr hWnd) |
| { |
| AssignHandle(hWnd); |
| } |
|
|
| public void AddHookProc(HookProc hook, Action removedHandler) |
| { |
| if (hooks == null) |
| { |
| hooks = new List<KeyValuePair<HookProc, Action>>(); |
| } |
| hooks.Insert(0, new KeyValuePair<HookProc, Action>(hook, removedHandler)); |
| } |
|
|
| public void RemoveHookProc(HookProc hook) |
| { |
| if (hooks != null) |
| { |
| for (int i = hooks.Count - 1; i >= 0; i--) |
| { |
| if (hooks[i].Key == hook) |
| { |
| hooks[i].Value?.Invoke(); |
| hooks.RemoveAt(i); |
| } |
| } |
| } |
| } |
|
|
| protected override void WndProc(ref Message m) |
| { |
| if (hooks != null) |
| { |
| foreach (var hook in hooks) |
| { |
| if (hook.Key(ref m)) return; |
| } |
| const int WM_NCDESTORY = 0x0082; |
| if (m.Msg == WM_NCDESTROY) |
| { |
| for (int i = hooks.Count - 1; i >= 0; i--) |
| { |
| hooks[i].Value?.Invoke(); |
| } |
| hooks = null; |
| } |
| base.WndProc(ref m); |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| public static void AddHook(IntPtr handle, HookProc hook, Action removedHandler = null) |
| { |
| if (!(NativeWindow.FromHandle(handle) is HookWindow window)) |
| { |
| window = new HookWindow(handle); |
| } |
| window.AddHookProc(hook, removedHandler); |
| } |
|
|
| |
| |
| |
| public static void RemoveHook(IntPtr handle, HookProc hook) |
| { |
| if (NativeWindow.FromHandle(handle) is HookWindow window) |
| { |
| window.RemoveHookProc(hook); |
| } |
| } |
| } |
| } |
轉(zhuǎn)自https://www.cnblogs.com/BluePointLilac/p/18802906