Skip to content

Instantly share code, notes, and snippets.

@abdusco
Created November 21, 2018 08:08
Show Gist options
  • Save abdusco/643d8b89e1cb3fddc9377fe7e3ff9169 to your computer and use it in GitHub Desktop.
Save abdusco/643d8b89e1cb3fddc9377fe7e3ff9169 to your computer and use it in GitHub Desktop.
Listen to workstation lock and unlock events in Python (tested on W10)
from collections import defaultdict
from enum import Enum
import win32api
import win32con
import win32gui
import win32ts
class SessionEvent(Enum):
ANY = 0
# window messages
CHANGE = 0x2B1 # WM_WTSSESSION_CHANGE
# WM_WTSSESSION_CHANGE events (wparam)
CONSOLE_CONNECT = 0x1 # WTS_CONSOLE_CONNECT
CONSOLE_DISCONNECT = 0x2 # WTS_CONSOLE_DISCONNECT
REMOTE_CONNECT = 0x3 # WTS_REMOTE_CONNECT
REMOTE_DISCONNECT = 0x4 # WTS_REMOTE_DISCONNECT
SESSION_LOGON = 0x5 # WTS_SESSION_LOGON
SESSION_LOGOFF = 0x6 # WTS_SESSION_LOGOFF
SESSION_LOCK = 0x7 # WTS_SESSION_LOCK
SESSION_UNLOCK = 0x8 # WTS_SESSION_UNLOCK
SESSION_REMOTE_CONTROL = 0x9 # WTS_SESSION_REMOTE_CONTROL
class WorkstationMonitor:
CLASS_NAME = "WorkstationMonitor"
WINDOW_TITLE = "Workstation Event Monitor"
def __init__(self):
self.window_handle = None
self.event_handlers = defaultdict(list)
self._register_listener()
def _register_listener(self):
wc = win32gui.WNDCLASS()
wc.hInstance = handle_instance = win32api.GetModuleHandle(None)
wc.lpszClassName = self.CLASS_NAME
wc.lpfnWndProc = self._window_procedure
window_class = win32gui.RegisterClass(wc)
style = 0
self.window_handle = win32gui.CreateWindow(window_class,
self.WINDOW_TITLE,
style,
0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
0, 0, handle_instance, None)
win32gui.UpdateWindow(self.window_handle)
# scope = win32ts.NOTIFY_FOR_THIS_SESSION
scope = win32ts.NOTIFY_FOR_ALL_SESSIONS
win32ts.WTSRegisterSessionNotification(self.window_handle, scope)
def listen(self):
win32gui.PumpMessages()
def stop(self):
exit_code = 0
win32gui.PostQuitMessage(exit_code)
def _window_procedure(self, window_handle: int, message: int, event_id, session_id):
"""
# WindowProc callback function
https://msdn.microsoft.com/en-us/library/ms633573(v=VS.85).aspx
"""
if message == SessionEvent.CHANGE.value:
self._handle_session_change(SessionEvent(event_id), session_id)
elif message == win32con.WM_CLOSE:
win32gui.DestroyWindow(window_handle)
elif message == win32con.WM_DESTROY:
win32gui.PostQuitMessage(0)
elif message == win32con.WM_QUERYENDSESSION:
return True
def _handle_session_change(self, event: SessionEvent, session_id: int):
for handler in self.event_handlers[event]:
handler(event)
for handler in self.event_handlers[SessionEvent.ANY]:
handler(event)
def register_handler(self, event: SessionEvent, handler: callable):
self.event_handlers[event].append(handler)
if __name__ == '__main__':
m = WorkstationMonitor()
m.register_handler(SessionEvent.ANY, handler=print)
m.listen()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment