捕捉最小化、最大化、调整大小、键盘焦点更改等

本文关键字:焦点 键盘 最小化 最大化 调整 | 更新日期: 2023-09-27 18:22:21

我目前正在编写一个应用程序,该应用程序需要捕获用户在窗口句柄上执行的每一个操作。

我需要提出以下事件:

  • 窗口大小调整、最大化、最小化或移动
  • 用户更改了活动窗口
  • 用户更改了窗口句柄的键盘焦点

为了做到这一点,我尝试了很多解决方案,但都无济于事。首先,我使用了一个Timer,它每100毫秒轮询一次前台窗口(使用GetForegroundWindow())和键盘焦点句柄,使用AttachThreadInput()和GetFocus()函数。

但是这个解决方案不是很方便,我更喜欢使用.NET Framework提供的UIAutomation的更干净的解决方案。但我意识到它占用了大量CPU,而且速度太慢,当我切换到另一个窗口句柄时,该事件有时会被调用3或4次。

关于窗口大小调整、最大化等,我也做了一个计时器(但不是很真实),并尝试使用一些挂钩技术,如CBT挂钩和Shell挂钩。不幸的是,我发现C#不支持这种钩子(全局钩子)。

我正在为我的程序的这一部分寻找一个稳定可靠的代码。提前谢谢。

捕捉最小化、最大化、调整大小、键盘焦点更改等

BrendanMcK在这篇文章中出色地回答了我的问题:

在Windows消息上设置挂钩

我把他的答案抄在下面。正如我所建议的,它比Timer更方便,而且它比UIAutomation消耗CPU更少。谢谢大家!

using System;
using System.Windows;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class NameChangeTracker
{
    delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType,
        IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
    [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);
    const uint EVENT_OBJECT_NAMECHANGE = 0x800C;
    const uint WINEVENT_OUTOFCONTEXT = 0;
    // Need to ensure delegate is not collected while we're using it,
    // storing it in a class field is simplest way to do this.
    static WinEventDelegate procDelegate = new WinEventDelegate(WinEventProc);
    public static void Main()
    {
        // Listen for name change changes across all processes/threads on current desktop...
        IntPtr hhook = SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE, IntPtr.Zero,
                procDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
        // MessageBox provides the necessary mesage loop that SetWinEventHook requires.
        // In real-world code, use a regular message loop (GetMessage/TranslateMessage/
        // DispatchMessage etc or equivalent.)
        MessageBox.Show("Tracking name changes on HWNDs, close message box to exit.");
        UnhookWinEvent(hhook);
    }
    static void WinEventProc(IntPtr hWinEventHook, uint eventType,
        IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
    {
        // filter out non-HWND namechanges... (eg. items within a listbox)
        if(idObject != 0 || idChild != 0)
        {
            return;
        }
        Console.WriteLine("Text of hwnd changed {0:x8}", hwnd.ToInt32()); 
    }
}