Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect active window changed using C# without polling

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

like image 736
Joe Avatar asked Dec 06 '10 23:12

Joe


2 Answers

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";     }  
like image 53
Stephen Lee Parker Avatar answered Oct 22 '22 01:10

Stephen Lee Parker


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!

like image 36
Savvas Sopiadis Avatar answered Oct 22 '22 00:10

Savvas Sopiadis