Skip to content

Commit

Permalink
Improved doc and fixed typos.
Browse files Browse the repository at this point in the history
  • Loading branch information
blep committed Sep 7, 2023
1 parent a0c12fe commit fb5d7c8
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 51 deletions.
64 changes: 41 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
# win32_window_monitor

Wraps WIN32 API [SetWinEventHook](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook) making it easy to monitor change of the focused window on Windows Operating
System. Provides helper function to easily retrieve process id and path of an event passed to the callback.
Monitor global window [events](https://learn.microsoft.com/en-us/windows/win32/winauto/event-constants)
on Windows O.S. using the [SetWinEventHook](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook)
WIN32 SDK API:

[SetWinEventHook](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook) reports global events containing the window HWND, pid and executable path to a python callback.
- Reports the event's window HWND, PID (process identifier) and executable path to a python callback.

# Example
- Provides helper functions to easily retrieve the PID and executable path from
the callback parameters.

The main.py shows how to use the API to produce the example output below. After
installing the `win32-window-monitor` package, the script `log_focused_window` is installed.
## Use cases

- Tracks the focused window and its process
- Tracks windows that capture the mouse or keyboard input
- Tracks which process is causing your fullscreen game to lose focus (Kevin Turner's initial motivation for his gist)

Check warning on line 16 in README.md

View workflow job for this annotation

GitHub Actions / test (windows-latest, 3.9, false)

Consider replacing term "his" with an alternative such as "their", "theirs"

README.md#L16 - Tracks which process is causing your fullscreen game to lose focus (Kevin Turner's initial motivation for his 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 script to log focus related events

`main.py` shows how to use the API to produce the example output below. After
installing the `win32-window-monitor` package, the script `log_focused_window` is installed (in
`venv\Scripts\log_focused_window.exe`, which is added to the `PATH` when activating the venv).

```bat
pip install win32-window-monitor
Expand All @@ -33,27 +48,30 @@ which produces the following output:
101314437:0.02 Focus W:0x440820 P:16088 T:7192 System32\conhost.exe C:\Windows\System32\cmd.exe - python -m win32_window_monitor.main
```

Columns content:
Columns content
----------------

- event time_ms : elapsed second since last event
- event time_ms : elapsed seconds since last event
- event
- W: HWND, the window handle
- P: process id
- T: thread id
- P: process ID
- T: thread ID
- short process path
- window title

Actions made to produce this event:
Actions made to produce those events
------------------------------------

- Bring Firefox window to focus by clicking on it in the Taskbar. Events with
`explorer.exe` are 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.

- Bring Firefox window to focus by click on it in the Taskbar. Events with `explorer.exe` are interactions with the
Taskbar.
- Bring Notion app to focus by click on it in the Taskbar.
- Bring back cmd.exe to focus by click on it in the Taskbar.

# Usage example
## Usage example

IMPORTANT: to track the current foreground window, you need at least HookEvent.SYSTEM_FOREGROUND and
HookEvent.SYSTEM_MINIMIZEEND (SYSTEM_FOREGROUND for is not send when restoring a minimized window).
HookEvent.SYSTEM_MINIMIZEEND (SYSTEM_FOREGROUND for is not sent when restoring a minimized window).

```python
from win32_window_monitor import *
Expand All @@ -72,15 +90,15 @@ def on_event(win_event_hook_handle: HWINEVENTHOOK, event_id: int, hwnd: wintypes
print(f'{event_time_ms} {event_id} P{process_id} {exe_path} {title}')

def main():
# - init_com(): initialize Windows COM (CoInitialize)
# - post_quit_message_on_break_signal: signal handlers to exit the
# application when CTRL+C or CTRL+Break is pressed.
# - 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.
with init_com(), post_quit_message_on_break_signal():
# Converts the callback to the ctype function type, and register it.
# Converts the callback to the ctypes function type, and register it.
win_event_proc = WinEventProcType(on_event)
event_hook_handle = set_win_event_hook(win_event_proc, HookEvent.SYSTEM_FOREGROUND)

# Run Windows message loop until WM_QUIT message is received (send by signal handlers above).
# Run the Windows message loop until the WM_QUIT message is received (sent by signal handlers above).
# If you have a graphic UI, it is likely that your application already has a Windows message
# loop that should be used instead.
run_message_loop()
Expand All @@ -91,7 +109,7 @@ if __name__ == '__main__':
main()
```

# Acknowledgment
## Acknowledgments

This project core is heavily based on the work of others:

Expand Down
56 changes: 38 additions & 18 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,35 @@
win32-window-monitor
====================

Monitor change of the focused window on Windows Operating System:
Monitor global window `events <https://learn.microsoft.com/en-us/windows/win32/winauto/event-constants>`_
on Windows O.S. using the `SetWinEventHook <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook>`_
WIN32 SDK API:

- Reports the focused window HWND, pid and executable path to a python callback
registered using `SetWinEventHook <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook>`_.
- Reports the event's window HWND and PID (process identifier) to a Python callback.

- Provides helpers function to easily retrieve process id and path from the
callback parameters.
- Provides helper functions to easily retrieve the PID and executable path
from the callback parameters.

Example
=======
Use cases
=========

- Tracks the focused window and its process
- Interact with the window or its process using the event's HWND or PID
- Tracks windows that capture the mouse or keyboard input
- Tracks which process is causing your fullscreen game to lose focus (Kevin Turner's initial motivation for his gist)

Check warning on line 24 in docs/index.rst

View workflow job for this annotation

GitHub Actions / test (windows-latest, 3.9, false)

Consider replacing term "his" with an alternative such as "their", "theirs"

docs/index.rst#L24 - Tracks which process is causing your fullscreen game to lose focus (Kevin Turner's initial motivation for his 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 script
=========================

``main.py`` shows how to use the API to produce the example output below. After
installing the ``win32-window-monitor`` package, the script ``log_focused_window`` is installed (in
``venv\Scripts\log_focused_window.exe``, which is added to the ``PATH`` when activating the venv).

The main.py shows how to use the API to produce the example output below. After
installing the ``win32-window-monitor`` package, the script ``log_focused_window``
is installed.

.. code-block:: batch
Expand All @@ -46,25 +61,30 @@ which produces the following output:
Columns content:

- event time_ms : elapsed second since last event
- event time_ms : elapsed seconds since last event
- event
- W: HWND, the window handle
- P: process id
- T: thread id
- short process path
- window title

Actions made to produce this event:
- Bring Firefox window to focus by click on it in the Taskbar. Events with `explorer.exe` are interactions with the Taskbar.
- Bring Notion app to focus by click on it in the Taskbar.
- Bring back cmd.exe to focus by click on it in the Taskbar.
Actions made to produce those events:

- Bring Firefox window to focus by clicking on it in the Taskbar. Events with
``explorer.exe`` are 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 example
=============

IMPORTANT: you need more than just SYSTEM_FOREGROUND to capture all
events that bring a window to the foreground. SYSTEM_FOREGROUND for
example is not generated when restoring a minimized window.
.. note::
To track the current foreground window, you need at least :class:`win32_window_monitor.HookEvent.SYSTEM_FOREGROUND`
and :class:`win32_window_monitor.HookEvent.SYSTEM_MINIMIZEEND`. (:class:`win32_window_monitor.HookEvent.SYSTEM_FOREGROUND`
is not sent when restoring a minimized window).


.. literalinclude:: ../win32_window_monitor/main_usage_example.py

Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "flit_core.buildapi"
name = "win32-window-monitor"
authors = [{name = "Baptiste Lepilleur", email = "baptiste.lepilleur@gmail.com"}]
readme = "README.md"
description = "Monitor/capture title and process of the focused desktop window using SetWinEventHook WIN32 API."
description = "Monitor/capture title and process of window global events using SetWinEventHook WIN32 API, making it easy to track the current focused window."
# See https://pypi.org/classifiers/ for all classifiers
classifiers = [
"Development Status :: 2 - Pre-Alpha",
Expand All @@ -29,6 +29,7 @@ dependencies = [
[project.urls]
Home = "https://github.com/blep/win32_window_monitor"
Source = "https://github.com/blep/win32_window_monitor"
Issues = "https://github.com/blep/win32_window_monitor/issues"
#Documentation = "https://?"

[project.scripts]
Expand Down
2 changes: 1 addition & 1 deletion win32_window_monitor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.1.0"
__version__ = "0.2.0"

from win32_window_monitor.ids import HookEvent, ObjectId, NamedInt
from win32_window_monitor.win32api import (
Expand Down
22 changes: 14 additions & 8 deletions win32_window_monitor/main_usage_example.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Usage example included in README.md.
"""Usage example included in doc.
Demonstrates setup/listen/teardown for an HookEvent.
"""

from win32_window_monitor import *
from ctypes import wintypes


def on_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,
Expand All @@ -16,21 +19,24 @@ def on_event(win_event_hook_handle: HWINEVENTHOOK, event_id: int, hwnd: wintypes
exe_path = get_process_filename(process_id) if process_id else '?'
print(f'{event_time_ms} {event_id} P{process_id} {exe_path} {title}')


def main():
# - init_com(): initialize Windows COM (CoInitialize)
# - post_quit_message_on_break_signal: signal handlers to exit the
# application when CTRL+C or CTRL+Break is pressed.
# - 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.
with init_com(), post_quit_message_on_break_signal():
# Converts the callback to the ctype function type, and register it.
# Converts the callback to the ctypes function type, and register it.
win_event_proc = WinEventProcType(on_event)
event_hook_handle = set_win_event_hook(win_event_proc, HookEvent.SYSTEM_FOREGROUND)

# Run Windows message loop until WM_QUIT message is received (send by signal handlers above).
# If you have a graphic UI, it is likely that your application already has a Windows message
# loop that should be used instead.
# 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()

0 comments on commit fb5d7c8

Please sign in to comment.