通过SendMessage或其他方式异步获取ForegroundWindow



当焦点从任何窗口更改为另一个窗口(甚至在窗口应用程序之间)时,是否有一种方法可以得到通知,以便在用户更改焦点时立即调用我的委托??

我一直在想,我可能只需要进行轮询:(:(每1秒调用一次GetForegroundWindow,但我真的不想这么做。

SetWinEventHook()可能是您的最佳选择;您可以监听EVENT_SYSTEM_FOREGROUND来监听前台窗口的更改,甚至可以监听EVENT_OJECT_FOCUS来监听应用程序和控件中更精细的焦点更改。

您需要将其与WINEVENT_OUTOFCONTEXT标志一起使用;这意味着更改通知将异步传递到您自己的应用程序,因此您不需要单独的DLL,但仍需要P/Invoke。但通知不会是即时-可能会有一个小的延迟-但这在异步中是隐含的。如果你想立即无延迟地完成某件事,你需要使用C++和进程内钩子(要么是带有WINEVENT_INNTEXT的SetWinEventHook,要么是SetSetWindowsHookEx风格的钩子。)

以下是一个似乎能满足您需求的示例:

using System;
using System.Windows;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class ForegroundTracker
{
            // Delegate and imports from pinvoke.net:
    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);
            // Constants from winuser.h
    const uint EVENT_SYSTEM_FOREGROUND = 3;
    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 foreground changes across all processes/threads on current desktop...
        IntPtr hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero,
                procDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
        // MessageBox provides the necessary mesage loop that SetWinEventHook requires.
        MessageBox.Show("Tracking focus, close message box to exit.");
        UnhookWinEvent(hhook);
    }
    static void WinEventProc(IntPtr hWinEventHook, uint eventType,
        IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
    {
        Console.WriteLine("Foreground changed to {0:x8}", hwnd.ToInt32()); 
    }
}

上面的例子就像冠军一样工作。我对它进行了一点重构,以生成一个可能有用的类。我没有定义所有的常量,所以如果你想捕获其他事件,你需要添加一些常量。

using System;
using System.Runtime.InteropServices;
#pragma warning disable 1591
// ReSharper disable InconsistentNaming
namespace MosaiqPerformanceMonitor {
     public class EventHook {
          public delegate void WinEventDelegate(
                IntPtr hWinEventHook, uint eventType,
                IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
          [DllImport("user32.dll")]
          public static extern IntPtr SetWinEventHook(
                uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc,
                uint idProcess, uint idThread, uint dwFlags);
          [DllImport("user32.dll")]
          public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
          public const uint EVENT_SYSTEM_FOREGROUND = 3;
          public const uint WINEVENT_OUTOFCONTEXT = 0;
          public const uint EVENT_OBJECT_CREATE = 0x8000;
          readonly WinEventDelegate _procDelegate;
          readonly IntPtr _hWinEventHook;
          public EventHook(WinEventDelegate handler, uint eventMin, uint eventMax) {
                _procDelegate = handler;
                _hWinEventHook = SetWinEventHook(eventMin, eventMax, IntPtr.Zero, handler, 0, 0, WINEVENT_OUTOFCONTEXT);
          }
          public EventHook(WinEventDelegate handler, uint eventMin)
                : this(handler, eventMin, eventMin) {
          }
          public void Stop() {
                UnhookWinEvent(_hWinEventHook);
          }
          // Usage Example for EVENT_OBJECT_CREATE (http://msdn.microsoft.com/en-us/library/windows/desktop/dd318066%28v=vs.85%29.aspx)
          // var _objectCreateHook = new EventHook(OnObjectCreate, EventHook.EVENT_OBJECT_CREATE);
          // ...
          // static void OnObjectCreate(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) {
          //    if (!Win32.GetClassName(hWnd).StartsWith("ClassICareAbout"))
          //        return;
          // Note - in Console program, doesn't fire if you have a Console.ReadLine active, so use a Form
     }
}

您可以安装一个Windows挂钩(需要一些p/Invoke)并监视发送到窗口的消息。此问题列出了将窗口置于前台所涉及的消息。以下是安装挂钩的MSDN文档

最新更新