post #1 of 1
Thread Starter 
Its been a while since I've posted a topic here - but today I was writing this very same code (in fact, I've written this very same code or used a similar technique many times) and I decided I would share it, for anyone who is interested.

This will be a quick introduction into Windows event hooking which is a type of hooking that applications can use to get information about various UI events that happen in the Windows system - these events are most commonly useful for Microsoft Active Accessibility or UI Automation. The event I will be demonstrating here will be the foreground window changed event, in which your application can get notified when a new window becomes foreground.

You can use this event to effectively create a history of foreground windows in your application - you get a window handle in the event. For me, I was developing an application that needed to remember the last active window before the user switched back to my application's window.

I was writing this application in C#, but I'm going to demonstrate the hooking technique in C. So, let's get with it. This assumes you know how to create an application in Win32 and C. This technique can be ported to .NET easily with a little bit of P/Invoke magic.


First start by importing the Windows header file:
Code:
#include <Windows.h>

We will use these two functions from user32.dll:
  • HWINEVENTHOOK WINAPI SetWinEventHook( _In_ UINT eventMin, _In_ UINT eventMax, _In_ HMODULE hmodWinEventProc, _In_ WINEVENTPROC lpfnWinEventProc, _In_ DWORD idProcess, _In_ DWORD idThread, _In_ UINT dwflags);
  • BOOL WINAPI UnhookWinEvent( _In_ HWINEVENTHOOK hWinEventHook);

We will use these constants:
  • WINEVENT_OUTOFCONTEXT (0x0000) - This specifies that the hook is not injected into process address spaces - instead the function is called back across process boundaries and runs asynchronously.
  • WINEVENT_SKIPOWNPROCESS (0x0002) - This specifies that we don't care about events generated by our own process (meaning that for this scenario, we will never see events for *our* windows becoming foreground).
  • EVENT_SYSTEM_FOREGROUND (0x0003) - The event ID that we will be listening for.

Next, define the callback hook procedure:
Code:
HWINEVENTHOOK  g_hWinEventHook = INVALID_HANDLE_VALUE; /* the handle to our event hook */
HWND g_hwndLastActiveWindow = INVALID_HANDLE_VALUE; /* we will store the last active window in a global variable */

VOID CALLBACK WinEventProcCallback( HWINEVENTHOOK hWinEventHook, 
        DWORD event, 
        HWND hwnd, 
        LONG idObject, 
        LONG idChild, 
        DWORD dwEventThread, 
        DWORD dwmsEventTime )
{
        /* hWinEventHook: The handle to the hook - we don't need this here */
        /* event: The event hat has triggered this callback */
        /* hwnd: The window that generated the event (in our case for foreground activation, this is a handle to the window that has received foreground) */
        /* idObject: The object ID that is associated with this event - we don't need this here */
        /* idChild: Identifies whether the event was triggered by the object itself, or a child of the object - we don't need this here */
        /* dwEventThread: The thread ID that generated the event, or the thread ID that owns the window - we don't need this here */
        /* dwmsEventTime: Specifies the time in milliseconds that the event was generated - we don't need this here */

        /* Test for a window foreground event */
        if ( event == EVENT_SYSTEM_FOREGROUND )
        {
                if ( hwnd && IsWindow( hwnd ) ) /* is this handle still valid? */
                {
                        /* Store the window handle in our global variable */
                        g_hwndLastActiveWindow = hwnd;
                }
        }
}

To install the hook, add the following to WM_CREATE on your main application window:
Code:
g_hWinEventHook = SetWinEventHook( EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, NULL, 
        WinEventProcCallback, 0, 0,
        WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS );

if ( !g_hWinEventHook )
{
        /* If SetWinEventHook fails, it will return zero - otherwise it returns a handle to the hook */
        /* ... if we get here we have failed */
}

To uninstall the hook, add the following to WM_DESTROY on your main application window:
Code:
if ( g_hWinEventHook != INVALID_HANDLE_VALUE )
{
        UnhookWinEvent( g_hWinEventHook ); 
}

That's all folks! Each time the foreground application changes, an event gets queued to our hook callback function. If the event is foreground activation, then we store the handle of the window that received foreground. Because we specify WINEVENT_SKIPOWNPROCESS, we never receive events for windows in our own application, meaning that we always reliably determine the last window to gain foreground activation.

smile.gif
Ol' Sandy
(28 items)
 
"Zeus"
(12 items)
 
Elite Preview
(6 items)
 
CPUMotherboardGraphicsRAM
Intel Xeon E3-1230v3 Gigabyte GA-Z97X-UD5H-BK MSI Gaming GTX 980 Kingston 32GB (4x8) 
Hard DriveHard DriveHard DriveHard Drive
Plextor PX-256M5S 256GB Samsung EVO 1TB Hitachi HDS721010CLA332 Hitachi HDS723020BLA642 
Hard DriveHard DriveHard DriveOptical Drive
Hitachi HDS723020BLA642 Hitachi HUA722010CLA330 WDC WD10EARS-00Z5B1 TSSTcorp CDDVDW SH-S223B 
CoolingCoolingOSMonitor
Phanteks PH-TC14PE with TY-140's Lamptron FCv5 (x2) Windows 8 Pro 64-bit Dell U2412M 
MonitorMonitorMonitorKeyboard
Dell U2412M Dell U2212HM Dell U2713HM Topre Realforce 87UB | Ducky DK9087 G2 Pro 
PowerCaseMouseMouse Pad
Corsair AX-750 Corsair Obsidian 650D Logitech G700 XTRAC Ripper XXL 
AudioAudioAudioAudio
Beyerdynamic DT-770 Pro 250ohm Schiit Bifrost DAC Schiit Asgard 2 HiVi Swan M50W 2.1 
CPUMotherboardRAMHard Drive
Intel Xeon E5-2620 Super Micro X9SRL-F-B 128GB 1333MHz LSI 9271-8i 
OSPowerCase
VMware ESXi 5.5 SeaSonic SS-400FL2 Fractal Define R3 
CPUMotherboardGraphicsRAM
Intel Core i5-3437U HP EliteBook Folio 9470m  Intel HD Graphics 4000  16GB DDR3 SDRAM 
Hard DriveOS
256GB SSD Windows 10 Insider Preview 
  hide details  
Reply
Ol' Sandy
(28 items)
 
"Zeus"
(12 items)
 
Elite Preview
(6 items)
 
CPUMotherboardGraphicsRAM
Intel Xeon E3-1230v3 Gigabyte GA-Z97X-UD5H-BK MSI Gaming GTX 980 Kingston 32GB (4x8) 
Hard DriveHard DriveHard DriveHard Drive
Plextor PX-256M5S 256GB Samsung EVO 1TB Hitachi HDS721010CLA332 Hitachi HDS723020BLA642 
Hard DriveHard DriveHard DriveOptical Drive
Hitachi HDS723020BLA642 Hitachi HUA722010CLA330 WDC WD10EARS-00Z5B1 TSSTcorp CDDVDW SH-S223B 
CoolingCoolingOSMonitor
Phanteks PH-TC14PE with TY-140's Lamptron FCv5 (x2) Windows 8 Pro 64-bit Dell U2412M 
MonitorMonitorMonitorKeyboard
Dell U2412M Dell U2212HM Dell U2713HM Topre Realforce 87UB | Ducky DK9087 G2 Pro 
PowerCaseMouseMouse Pad
Corsair AX-750 Corsair Obsidian 650D Logitech G700 XTRAC Ripper XXL 
AudioAudioAudioAudio
Beyerdynamic DT-770 Pro 250ohm Schiit Bifrost DAC Schiit Asgard 2 HiVi Swan M50W 2.1 
CPUMotherboardRAMHard Drive
Intel Xeon E5-2620 Super Micro X9SRL-F-B 128GB 1333MHz LSI 9271-8i 
OSPowerCase
VMware ESXi 5.5 SeaSonic SS-400FL2 Fractal Define R3 
CPUMotherboardGraphicsRAM
Intel Core i5-3437U HP EliteBook Folio 9470m  Intel HD Graphics 4000  16GB DDR3 SDRAM 
Hard DriveOS
256GB SSD Windows 10 Insider Preview 
  hide details  
Reply