You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C#结合user32.dll监听进程内嵌套窗口的尺寸变更事件

解决嵌套子窗口尺寸变更监听问题

你的代码核心问题在于SetWinEventHook参数误用,以及缺少对目标子窗口的过滤判断,以下是具体修复方案:

关键问题分析

  1. SetWinEventHook的第三个参数hmodWinEventProc被错误传入了窗口句柄,该参数仅在钩子函数位于外部DLL时才需要传入模块句柄,本地进程内的钩子应传入IntPtr.Zero。
  2. 未在回调中过滤目标窗口,导致所有窗口的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 15:23:28