pythonservicewindows-11user-activity

How can I watch for user activity from a service (session 0) in Windows 11?


I came up with the solution below to watch for user activity from a service. This is working as expected in Windows 10, but doesn't work for mouse activity in Windows 11 (only key press/release events are detected); evidently something has changed WRT the behavior of csrss.exe under Windows 11.

I've looked into all of the available information returned by psutil for csrss.exe and wasn't able to come up with an alternative approach to monitoring mouse activity in Windows 11. Ultimately, my question is: How can I watch for user activity from a service (session 0) in Windows 11?

I'm trying to find an approach that I can use either in conjunction with or instead of my existing solution - any help/info is much appreciated!

More info on this can be found here in a Q/A detailing my initial solution.

import psutil


def get_csrss_pids() -> list[int]:
    """Get the PID(s) for the Windows Client Server Runtime process"""
    return [
        proc.pid for proc in psutil.process_iter(attrs=['name'])
        if proc.name() == 'csrss.exe'
    ]


def get_io(pids: list[int]) -> list[int]:
    """Returns the last `read_bytes` value for the given csrss.exe PID(s)"""
    return [psutil.Process(pid).io_counters().read_bytes for pid in pids]


pids = get_csrss_pids()
last_io = get_io(pids)  # store an initial value to compare against

while True:
    try:
        if (tick := get_io(pids)) != last_io:  # if activity is detected...
            print(tick)  # do something
            last_io = tick  # store the new value to compare against
    except KeyboardInterrupt:
        break

Solution

  • In Windows, keyboard and mouse input is associated with the interactive window station. This window station has a well-known name, "WinSta0".

    As you've figured out, WinSta0 is the window station of an interactive session, which is definitely not session 0. Services are not interactive. This means you cannot call OpenWindowStationA("WinSta0") from a service.

    This is intentional. Since Windows XP, Microsoft has been continuously increasing security. User Interface Isolation has been in Windows since Windows Vista. It seems that Windows 11 has taken yet another step, and that's not exactly surprising. csrss.exe is an implementation detail of Windows; it's currently a per-session process. (That's why you could find multiple). Only the csrss.exe of the WinSta0 session would deal with keyboard and mouse, previously.

    Logically, that means that the other csrss processes had unnecessary code. It only makes sense to refactor csrss into interactive and non-interactive parts, so the Windows 11 change is not that weird.

    Given that Windows changes and continues to change, the logical answer is to take one step back. If you need the interactive window station in one way or another, then you need to run code in that session. That means it does not run as a service. Your service can start another process in another session, and communicate with that second process, but it has to be isolated.