Detect if Windows workstation is locked in PyQt5 application
Question: I have a PyQt5 application, which I would like to check if the Windows
workstation is in a locked state or not.

At first, I have tried to use snippet [See if my workstation is
locked](http://timgolden.me.uk/python/win32_how_do_i/see_if_my_workstation_is_locked.html).
It did not work at all on my Windows 7 64-bit. It thinks that workstation is
locked all the time.

I have noticed in SO question [How to detect Windows is
locked?](http://stackoverflow.com/questions/8606300/how-to-detect-windows-is-
locked) that the above solution is probably a hack and I should use
`WTSRegisterSessionNotification`. I have found the following snippet [Terminal
Services event monitor for Windows
NT/XP/2003/...](https://gist.github.com/grawity/896881). It works fine used as
is.

I have simplified the code to the following:

    
    
    import win32con
    import win32gui
    import win32ts
    
    
    WM_WTSSESSION_CHANGE        = 0x2B1
    
    
    class WTSMonitor():
        className = "WTSMonitor"
        wndName = "WTS Event Monitor"
    
        def __init__(self):
            wc = win32gui.WNDCLASS()
            wc.hInstance = hInst = win32gui.GetModuleHandle(None)
            wc.lpszClassName = self.className
            wc.lpfnWndProc = self.WndProc
            self.classAtom = win32gui.RegisterClass(wc)
    
            style = 0
            self.hWnd = win32gui.CreateWindow(self.classAtom, self.wndName,
                style, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
                0, 0, hInst, None)
            win32gui.UpdateWindow(self.hWnd)
            win32ts.WTSRegisterSessionNotification(self.hWnd, win32ts.NOTIFY_FOR_ALL_SESSIONS)
    
        def start(self):
            win32gui.PumpMessages()
    
        def stop(self):
            win32gui.PostQuitMessage(0)
    
        def WndProc(self, hWnd, message, wParam, lParam):
            if message == WM_WTSSESSION_CHANGE:
                self.OnSession(wParam, lParam)
    
        def OnSession(self, event, sessionID):
            print(event)
    
    if __name__ == '__main__':
        m = WTSMonitor()
        m.start()
    

Now I am trying to merge it with PyQt5 skeleton:

    
    
    import sys
    from PyQt5.QtWidgets import *
    
    class Window(QWidget):
        def __init__(self, *args, **kwargs):
            QWidget.__init__(self, *args, **kwargs)
    
            self.show()
    
    if __name__ == '__main__':    
        app = QApplication(sys.argv)
        win = Window()
        sys.exit(app.exec_())
    

However, I am not sure how to do that. Every attempt I have made did not work,
the event does not seem to be registered. Any idea how to make this work?

**Edit:** This is one of the merges I have tried.

    
    
    import sys
    from PyQt5.QtWidgets import *
    import win32gui
    import win32ts
    
    
    WM_WTSSESSION_CHANGE        = 0x2B1
    
    
    class WTSMonitor(QWidget):
        def __init__(self, *args, **kwargs):
            QWidget.__init__(self, *args, **kwargs)    
            self.show()
            win32ts.WTSRegisterSessionNotification(self.winId(), win32ts.NOTIFY_FOR_ALL_SESSIONS)
    
        def start(self):
            win32gui.PumpMessages()
    
        def stop(self):
            win32gui.PostQuitMessage(0)
    
        def WndProc(self, hWnd, message, wParam, lParam):
            if message == WM_WTSSESSION_CHANGE:
                self.OnSession(wParam, lParam)
    
        def OnSession(self, event, sessionID):
            print(event)
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = WTSMonitor()
        win.start()
        sys.exit(app.exec_())
    


Answer: I have adapted ideas from
[WndProcHookMixin.py](http://wiki.wxpython.org/HookingTheWndProc?action=AttachFile&do=view&target=WndProcHookMixin.py)
posted on [wxPython wiki](http://wiki.wxpython.org/HookingTheWndProc) to PyQt.
It works as expected.

    
    
    import sys
    
    from PyQt5.QtWidgets import *
    import win32api
    import win32con
    import win32gui
    import win32ts
    
    
    WM_WTSSESSION_CHANGE = 0x2B1
    WTS_SESSION_LOCK = 0x7
    WTS_SESSION_UNLOCK = 0x8 
    
    
    # http://wiki.wxpython.org/HookingTheWndProc
    # http://wiki.wxpython.org/HookingTheWndProc?action=AttachFile&do=view&target=WndProcHookMixin.py
    # http://wiki.wxpython.org/HookingTheWndProc?action=AttachFile&do=view&target=WndProcHookMixinCtypes.py
    class WndProcHookMixin:
        def __init__(self):
            self.msgDict = {}
    
        def hookWndProc(self):
            self.oldWndProc = win32gui.SetWindowLong(self.winId(), win32con.GWL_WNDPROC, self.localWndProc)
    
        def unhookWndProc(self):
            win32api.SetWindowLong(self.winId(), win32con.GWL_WNDPROC, self.oldWndProc)
    
        def addMsgHandler(self, messageNumber, handler):
            self.msgDict[messageNumber] = handler
    
        def localWndProc(self, hWnd, msg, wParam, lParam):
            if msg in self.msgDict:
                if self.msgDict[msg](wParam, lParam) == False:
                    return
    
            if msg == win32con.WM_DESTROY: 
                self.unhookWndProc()
    
            return win32gui.CallWindowProc(self.oldWndProc, hWnd, msg, wParam, lParam)
    
    
    class Window(QWidget, WndProcHookMixin):
        def __init__(self, *args, **kwargs):
            QWidget.__init__(self, *args, **kwargs)    
            self.show()
            win32ts.WTSRegisterSessionNotification(self.winId(), win32ts.NOTIFY_FOR_ALL_SESSIONS)
            self.addMsgHandler(WM_WTSSESSION_CHANGE, self.on_session)
            self.hookWndProc()
    
        def on_session(self, wParam, lParam):
            event, session_id = wParam, lParam
            if event == WTS_SESSION_LOCK:
                print("Locked")
            if event == WTS_SESSION_UNLOCK:
                print("Unlocked")
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = Window()
        sys.exit(app.exec_())
    

