Асинхронно GetForegroundWindow через SendMessage или что-то? - PullRequest
13 голосов
/ 12 января 2012

Есть ли способ получать уведомления о смене фокуса из любого окна в другое окно (даже между окнами приложений), чтобы я мог просто немедленно вызвать моего делегата, когда пользователь меняет фокус ??

Iпродолжай думать, что мне, возможно, просто придется опрашивать :( :( каждую 1 секунду и вызывать GetForegroundWindow, но я действительно не хочу этого делать.

Ответы [ 3 ]

23 голосов
/ 13 января 2012

SetWinEventHook () , вероятно, ваш лучший выбор; вы можете прослушать EVENT_SYSTEM_FOREGROUND для прослушивания изменений окна переднего плана или даже EVENT_OBJECT_FOCUS для прослушивания более точных изменений фокуса в приложениях и в элементах управления.

Вам нужно будет использовать это с флагом WINEVENT_OUTOFCONTEXT; это означает, что уведомление об изменении будет доставлено асинхронно в ваше собственное приложение, поэтому вам не понадобится отдельная DLL - вам все равно потребуется P / Invoke. Но уведомление не будет мгновенным - может быть небольшая задержка - но это подразумевается с асинхронным . Если вы хотите сделать что-то абсолютно немедленно без каких-либо задержек, вам нужно будет использовать C ++ и обработчик внутри процесса (либо SetWinEventHook с WINEVENT_INCONTEXT, либо перехват в стиле 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()); 
    }
}
14 голосов
/ 14 августа 2012

Пример выше работает как чемпион. Я немного изменил рефакторинг, чтобы создать класс, который может быть полезен. Я не определил все константы, поэтому вам нужно добавить несколько, если вы хотите перехватывать другие события.

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
     }
}
3 голосов
/ 12 января 2012

Вы можете установить хук Windows (требуется немного P / Invoke) и следить за сообщениями, отправляемыми в Windows. В этом вопросе перечислены сообщения, связанные с выводом окна на передний план. Вот документация MSDN для установки хука

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...