2012-01-12 30 views
12

¿Hay alguna manera de ser notificado de cuándo cambia el foco de una ventana a otra (incluso entre aplicaciones de Windows) de modo que pueda hacer que se llame inmediatamente a mi delegado cuando el usuario cambie de foco?asincrónicamente GetForegroundWindow a través de SendMessage o algo?

sigo pensando que sólo puede tener que hacer el sondeo :(:(cada 1 segundo y llame GetForegroundWindow pero realmente no quiero hacer eso.

+0

SetWindowsHookEx() con un gancho WH_SHELL le proporciona una notificación. Difícil de hacer en C#. –

+0

No hay [es necesario agregar una firma adicional] (http://stackoverflow.com/faq#signatures) a su publicación. – Deanna

Respuesta

20

SetWinEventHook() es probablemente su mejor apuesta;.. se puede escuchar a cualquiera de los EVENT_SYSTEM_FOREGROUND para escuchar cambios de ventana en primer plano, o incluso EVENT_OBJECT_FOCUS para escuchar más cambios de enfoque de grano fino dentro de las aplicaciones y dentro de los controles.

Deberá usar esto con el indicador WINEVENT_OUTOFCONTEXT; esto significa que la notificación de cambio se enviará de forma asíncrona a su propia aplicación, por lo que no necesitará una DLL por separado; sin embargo, necesitará P/Invocar. Pero la notificación no será instantánea - puede haber un pequeño retraso - pero eso está implícito con asincrónico. Si desea hacer algo de manera inmediata sin demora alguna, necesitará usar C++ y un enlace en proceso (SetWinEventHook con WINEVENT_INCONTEXT o el gancho del estilo SetSetWindowsHookEx).

Aquí hay una muestra que parece para hacer lo que estás buscando:

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()); 
    } 
} 
3

Se puede instalar un gancho de Windows (requiere un poco de P/invocar) y ver los mensajes enviados a las ventanas This question se relacionan los mensajes relacionados con llevar una ventana al primer plano Here is the MSDN documentation para instalar un gancho

13

El ejemplo anterior funciona como un campeón. Refactoré un poco para hacer una clase que podría ser útil. No definí todas las constantes, por lo que necesitaría agregar algunas si desea atrapar otros eventos.

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

Lo voté porque tengo que investigar mucho para aprender a usar el constructor. Y el pseudocódigo muy incompleto al final no ayuda. Además, necesita formularios. Muy hacky. –

Cuestiones relacionadas