asynchronously GetForegroundWindow via SendMessage or something?

后端 未结 3 1259
一向
一向 2020-12-08 12:00

Is there a way to be notified of when focus changes from any window to another window(even between windows applications) such that I can just have my delegate called immedia

相关标签:
3条回答
  • You could install a Windows hook (requires some P/Invoke) and watch for messages sent to the windows. This question lists the messages involved in bringing a window to the foreground. Here is the MSDN documentation for installing a hook

    0 讨论(0)
  • 2020-12-08 12:16

    SetWinEventHook() is probably your best bet; you can listen to either the EVENT_SYSTEM_FOREGROUND to listen for foreground window changes - or even EVENT_OBJECT_FOCUS to listen for more fine-grain focus changes within apps and within controls.

    You'll need to use this with the WINEVENT_OUTOFCONTEXT flag; this means that the change notification will be delivered asynchronously to your own app, so you won't need a separate DLL - you'll still need to P/Invoke though. But the notification won't be instant - there may be a small delay - but that's implied with asynchronous. If you want to do something absolutely immediately with no delay whatsoever, you're going to need to use C++ and an in-process hook (either SetWinEventHook with WINEVENT_INCONTEXT or the SetSetWindowsHookEx-style hook.)

    Here's a sample that seems to do what you're looking for:

    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()); 
        }
    }
    
    0 讨论(0)
  • 2020-12-08 12:17

    The example above works like a champ. I refactored it a little to make a class that might be useful. I didn't define all the constants, so you'd need to add a few if you want to trap other events.

    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
         }
    }
    
    0 讨论(0)
提交回复
热议问题