Package: win32-window-monitor
win32-window-monitorMonitor global windoweventson Windows O.S. using theSetWinEventHookWIN32 SDK API:Reports the event’s window HWND and PID (process identifier) to a Python callback.Provides helper functions to easily retrieve the PID and executable path
from the callback parameters.Use casesTracks the focused window and its processTracks windows that capture the mouse or keyboard inputTracks which process is causing your fullscreen game to lose focus (Kevin Turner’s initial motivation for the gist)… (there are lots of UI Automation related events that could be useful)Since the standard HWND and PID are readily available, you can utilize
existing Python modules to interact with either the window or the process.log_focused_window scriptwin32_window_monitor/main.pyshows how to use the API to produce the example output below. After
installing thewin32-window-monitorpackage, the scriptlog_focused_windowis installed (invenv\Scripts\log_focused_window.exe, which is added to thePATHwhen activating the venv).pip install win32-window-monitor
log_focused_windowwhich produces the following output:101307546:0.00  Capture         W:0x1014c       P:7440          T:7592          Windows\explorer.exe    Running applications
101307687:0.14  Foreground      W:0x903ce       P:2508          T:1988          net\firefox.exe blep/win32_window_monitor: Monitor change of the focused window on Windows O.S.. Reports the focused window HWND, pid and executable to a Python callback. — Mozilla Firefox
101307687:0.00  Focus           W:0x903ce       P:2508          T:1988          net\firefox.exe blep/win32_window_monitor: Monitor change of the focused window on Windows O.S.. Reports the focused window HWND, pid and executable to a Python callback. — Mozilla Firefox
101312015:4.33  Show            W:0xc080c       P:7440          T:7592          Windows\explorer.exe
101312015:0.00  Show            W:0x10150       P:7440          T:7592          Windows\explorer.exe
101312031:0.02  Show            W:0x1065e       P:12428         T:11660         Notion\Notion.exe       Chrome Legacy Window
101312312:0.28  Capture         W:0x1014c       P:7440          T:7592          Windows\explorer.exe    Running applications
101312468:0.16  Foreground      W:0x20642       P:12428         T:11660         Notion\Notion.exe       Python: automated CI/Release/doc/Wheel
101312484:0.02  Focus           W:0x20642       P:12428         T:11660         Notion\Notion.exe       Python: automated CI/Release/doc/Wheel
101312484:0.00  Show            W:0x20642       P:12428         T:11660         Notion\Notion.exe       Python: automated CI/Release/doc/Wheel
101314312:1.83  Capture         W:0x1014c       P:7440          T:7592          Windows\explorer.exe    Running applications
101314421:0.11  Foreground      W:0x440820      P:16088         T:7192          System32\conhost.exe    C:\Windows\System32\cmd.exe - python  -m win32_window_monitor.main
101314437:0.02  Focus           W:0x440820      P:16088         T:7192          System32\conhost.exe    C:\Windows\System32\cmd.exe - python  -m win32_window_monitor.mainColumns content:event time_ms : elapsed seconds since last eventeventW: HWND, the window handleP: process idT: thread idshort process pathwindow titleActions made to produce those events:Bring Firefox window to focus by clicking on it in the Taskbar. Events withexplorer.exeare interactions with the Taskbar.Bring Notion app to focus by clicking on it in the Taskbar.Bring back cmd.exe to focus by clicking on it in the Taskbar.Usage exampleIMPORTANT: To track the current foreground window, you need at leastHookEvent.SYSTEM_FOREGROUNDandHookEvent.SYSTEM_MINIMIZEEND(HookEvent.SYSTEM_FOREGROUNDis not sent when
restoring a minimized window).# win32_window_monitor/main_usage_example.pyfromwin32_window_monitorimport*fromctypesimportwintypesdefon_event(win_event_hook_handle:HWINEVENTHOOK,event_id:int,hwnd:wintypes.HWND,id_object:wintypes.LONG,id_child:wintypes.LONG,event_thread_id:wintypes.DWORD,event_time_ms:wintypes.DWORD):# Called from the thread running the Windows message loop.# HookEvent provides a nice str, and unlike enum.Enum accepts any integer value.event_id=HookEvent(event_id)title=get_window_title(hwnd)process_id=get_hwnd_process_id(event_thread_id,hwnd)exe_path=get_process_filename(process_id)ifprocess_idelse'?'print(f'{event_time_ms}{event_id}P{process_id}{exe_path}{title}')defmain():# - init_com(): Initialize Windows COM (CoInitialize)# - post_quit_message_on_break_signal: Signal handlers to exit the# application when CTRL+C or CTRL+Break are pressed.withinit_com(),post_quit_message_on_break_signal():# We must keep the event_hook_handle alive. Failure to do that may# cause a crash as the "trampoline" function generated by ctypes for# the event hook would be deleted.event_hook_handle=set_win_event_hook(on_event,HookEvent.SYSTEM_FOREGROUND)# Run the Windows message loop until the WM_QUIT message is received# (sent by signal handlers above). If you have a graphic UI (TkInter, Qt...), it is# likely that your application already has a Windows message loop that# should be used instead.run_message_loop()unhook_win_event(event_hook_handle)if__name__=='__main__':main()AcknowledgmentsThis project core is heavily based on the work of others:Kevin Turner:GISTproviding an excellent starting base for this project.Eric Blade:getting process name from window handleDavid Heffernan:using WIN32 SetWinEventHook with ctypes