如何用C#结合user32.dll监听进程内嵌套窗口的尺寸变更事件
解决嵌套子窗口尺寸变更监听问题
你的代码核心问题在于SetWinEventHook参数误用,以及缺少对目标子窗口的过滤判断,以下是具体修复方案:
关键问题分析
SetWinEventHook的第三个参数hmodWinEventProc被错误传入了窗口句柄,该参数仅在钩子函数位于外部DLL时才需要传入模块句柄,本地进程内的钩子应传入IntPtr.Zero。- 未在回调中过滤目标窗口,导致所有窗口的
MOVESIZEEND事件都被触发,只捕获到主窗口是因为主窗口的事件更显眼。
修改后的代码
class Win32WindowEvents { // 确保EventTypes枚举值正确:EVENT_SYSTEM_MOVESIZEEND = 0x000B private enum EventTypes : uint { EVENT_SYSTEM_MOVESIZEEND = 0x000B } private enum WinHookParameter : uint { OUTOFCONTEXT = 0x0000 } [DllImport("user32.dll")] static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); [DllImport("user32.dll")] static extern bool UnhookWinEvent(IntPtr hWinEventHook); // 补充WinEventDelegate定义(原代码可能遗漏) private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); private Win32Window Window_toListen; private IntPtr _resizeHook; private IntPtr _reorderHook; private WinEventDelegate _resizeProc; private WinEventDelegate _reorderProc; private uint timelogged; // 补充变量定义 // 假设Resizing是一个外部事件或方法 public event Action<Process, Win32Window, EventTypes> Resizing; public Win32WindowEvents(Win32Window window) { StopListening(); _resizeProc = new WinEventDelegate(WinEventProc_Resizing); _reorderProc = new WinEventDelegate(WinEventProc_Reordering); Window_toListen = window; // 获取目标窗口的进程ID和线程ID(可选,缩小监听范围) uint processId = 0; uint threadId = GetWindowThreadProcessId(window.hWnd, out processId); // 修正SetWinEventHook参数:hmodWinEventProc传IntPtr.Zero,指定进程/线程缩小监听范围 _resizeHook = SetWinEventHook( (uint)EventTypes.EVENT_SYSTEM_MOVESIZEEND, (uint)EventTypes.EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, // 本地钩子,模块句柄为0 _resizeProc, processId, // 目标窗口所属进程ID threadId, // 目标窗口所属线程ID (uint)WinHookParameter.OUTOFCONTEXT ); } private void WinEventProc_Resizing(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { // 关键:只处理目标子窗口的事件 if (hwnd != Window_toListen.hWnd) return; if (Math.Abs(timelogged - dwmsEventTime) < 1000) return; timelogged = dwmsEventTime; Resizing?.Invoke(Window_toListen.Process, Window_toListen, (EventTypes)eventType); } private void WinEventProc_Reordering(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { // 同理,需要时添加目标窗口过滤 } public void StopListening() { // 卸载所有钩子,避免资源泄漏 if (_resizeHook != IntPtr.Zero) { UnhookWinEvent(_resizeHook); _resizeHook = IntPtr.Zero; } if (_reorderHook != IntPtr.Zero) { UnhookWinEvent(_reorderHook); _reorderHook = IntPtr.Zero; } } // 补充获取窗口进程/线程ID的API [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); }
额外说明
- 补充了
GetWindowThreadProcessId来获取目标窗口的进程和线程ID,传入SetWinEventHook可以缩小监听范围,避免处理无关事件,提升性能。 - 在回调中增加了
hwnd != Window_toListen.hWnd的判断,确保只响应目标子窗口的尺寸变更事件。 - 修正了
StopListening方法,确保所有钩子都被正确卸载,防止资源泄漏。 - 补充了
WinEventDelegate的定义(原代码可能遗漏,否则编译会报错)。
内容的提问来源于stack exchange,提问作者Wiley Ng
相关产品推荐
相关产品推荐

