Detect active window changed using C# without polling
Solution 1
Create a new windows forms project, add a textbox, make it multiline, and set the textbox Dock property to fill, name it Log and paste in the following code (you'll need to add System.Runtime.InteropServices to your usings)...
WinEventDelegate dele = null;
public Form1()
{
InitializeComponent();
dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
}
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);
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Log.Text += GetActiveWindowTitle() + "\r\n";
}
Solution 2
I know this thread is old, but for sake of future use: when running the code you'll notice a crash after a while. This is caused from the line in the Form constructor:
public Form1()
{
InitializeComponent();
WinEventDelegate dele = new WinEventDelegate(WinEventProc);//<-causing ERROR
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
}
Instead of the above make the following modification:
public Form1()
{
InitializeComponent();
dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
}
WinEventDelegate dele = null;
..works now as expected!
Solution 3
You can use SetWinEventHook
and listen for the EVENT_SYSTEM_FOREGROUND
event. Use the WINEVENT_OUTOFCONTEXT
flag to avoid the global-hook problem.
Joe
Updated on December 05, 2020Comments
-
Joe over 3 years
How might one invoke a callback whenever the current active window changes. I've seen how it might be done using CBTProc. However, global events aren't easy to hook into with managed code. I'm interested in finding a way that doesn't require polling. I'd prefer an event driven approach.
Regards
-
Ehsan Zargar Ershadi about 12 yearsCan you give some examples , please.
-
Raymond Chen about 12 years
-
Kenneth K. about 11 yearsAs per Savvas Sopiadis' answer, the scope of
dele
needs to be widened to prevent the delegate from being disposed. See the comments on the documentation page for more info. -
Anthony Raimondo almost 9 yearsThis doesnt seem to work with a console application, does anyone know why? I removed InitializeComponent().
-
public wireless almost 9 yearsAdditional info: Your out of context function must be fast. (msdn.microsoft.com/en-us/library/windows/desktop/…)
-
public wireless almost 9 years@AnthonyRaimondo Apparently it 'doesn't fire if you have a Console.ReadLine active'. See stackoverflow.com/questions/8840926/…
-
Wobbles over 7 yearsTo reduce interop calls and clean up code a bit;
GetForegroundWindow()
is not needed asWinEventProc
already receives the foreground handle as ashwnd
which can be used to get the title. -
NtFreX almost 7 yearsYou should also call
UnhookWinEvent
in the end. -
IneedHelp over 5 yearsToo bad this doesn't work properly when resorting windows (from the minimized state)..
-
Walter Stabosz over 2 yearsIt crashes because when
WinEventDelegate dele
is scoped inside of theForm1
constructor, it is a candidate for garbage collection since the reference is lost at the end of the constructor. Scoping the at the class level will keep the reference alive until the class object is disposed. -
A Petrov over 2 yearsThis does not work in a windows service.