WinUI3主窗口WM_MOUSEMOVE事件捕获失败问题排查
无法捕获WM_MOUSEMOVE消息的原因及解决方法
核心原因
- WinUI3消息路由机制限制:WinUI3基于XAML框架,鼠标移动这类输入消息会被XAML输入系统优先拦截,转换为
PointerMoved等XAML原生事件,不会传递到你Hook的顶层窗口过程。 - 窗口层级结构问题:你Hook的是WinUI3窗口的顶层HWND,但客户区的鼠标消息实际发送给顶层窗口内部的XAML内容宿主子窗口,你的顶层窗口过程根本接收不到这些消息。
解决方案
方案一:使用XAML原生事件(推荐)
WinUI3框架提供了原生输入事件,完全可以替代Win32消息的方式,代码更简洁且符合框架设计:
public sealed partial class MainWindow : Window { private readonly WindowProcedureHook _hook; public MainWindow() { this.InitializeComponent(); _hook = new WindowProcedureHook(this, WndProc); // 订阅PointerMoved事件 this.PointerMoved += MainWindow_PointerMoved; GetAppWindowAndPresenter(); _presenter.IsMaximizable = true; _presenter.IsMinimizable = true; _apw.Title = "Title"; } private void MainWindow_PointerMoved(object sender, PointerRoutedEventArgs e) { // 获取鼠标在窗口内的坐标 var point = e.GetCurrentPoint(this).Position; // 这里添加你的处理逻辑 System.Diagnostics.Debug.WriteLine($"鼠标位置:X={point.X}, Y={point.Y}"); } // 其他原有代码... }
方案二:Hook XAML内容宿主窗口(不推荐)
如果必须使用Win32消息机制,需要找到顶层窗口内部的XAML内容宿主子窗口并Hook它的过程,但这种方法依赖WinUI3内部实现,可能随版本变更失效:
// 在MainWindow构造函数中添加 var mainHwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); // 查找XAML内容宿主窗口 var contentHwnd = FindWindowEx(mainHwnd, IntPtr.Zero, "Windows.UI.Core.CoreWindow", null); if (contentHwnd != IntPtr.Zero) { // 为子窗口创建Hook,需要修改WindowProcedureHook类支持传入自定义HWND var contentHook = new WindowProcedureHook(contentHwnd, ContentWndProc); } // 子窗口的消息处理函数 private IntPtr? ContentWndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam) { const int WM_MOUSEMOVE = 0x0200; if (msg == WM_MOUSEMOVE) { // 这里可以捕获到鼠标移动消息 System.Diagnostics.Debug.WriteLine("捕获到WM_MOUSEMOVE"); return null; } return null; } // 需要在WindowProcedureHook中添加支持自定义HWND的构造函数 public WindowProcedureHook(IntPtr hwnd, Func<IntPtr, int, IntPtr, IntPtr, IntPtr?> callback) { ArgumentNullException.ThrowIfNull(callback); _wndProc = WndProc; _callback = callback; const int GWLP_WNDPROC = -4; _prevProc = GetWindowLong(hwnd, GWLP_WNDPROC); SetWindowLong(hwnd, GWLP_WNDPROC, Marshal.GetFunctionPointerForDelegate(_wndProc)); } // 补充FindWindowEx的DllImport [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
内容的提问来源于stack exchange,提问作者user1094779
相关产品推荐
相关产品推荐

