Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asynchronously GetForegroundWindow via SendMessage or something?

Tags:

c#

winapi

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 immediately when the user changes focus??

I keep thinking I may just have to do polling :( :( every 1 second and call GetForegroundWindow but I really don't want to do that.

like image 938
Dean Hiller Avatar asked Jan 12 '12 19:01

Dean Hiller


1 Answers

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()); 
    }
}
like image 171
BrendanMcK Avatar answered Sep 21 '22 07:09

BrendanMcK