I've written a Windows service in Python that needs to be able to detect user activity. Under normal circumstances I would call the Windows GetLastInputInfo method, but that method doesn't work when called by a service. Here's the relevant info from the documentation for this method:
This function is useful for input idle detection. However, GetLastInputInfo does not provide system-wide user input information across all running sessions. Rather, GetLastInputInfo provides session-specific user input information for only the session that invoked the function.
The salient point is this: "for only the session that invoked the function"
If called by the service, GetLastInputInfo will always return 0 because the service is running in session 0 and doesn't receive input! How can my service detect user activity from the console session?
Caveats Up Front:
This solution is tested and working under Windows 10.
I was able to do some limited testing of this solution under Windows 11, and it seems like mouse activity (button presses and motion) is not registered, but key press and key release events are. If all you need is key activity detection, this will work. But if you need to monitor mouse events, you're out of luck pending a different/updated solution.
If anyone has an alternative solution for Windows 11, I'm all ears!
Fortunately, there's a workaround for this problem! While you can't poll for user activity directly from a service, you can check to see if the system is currently handling user input by querying the I/O info for the Windows Client Server Runtime process (a.k.a. "csrss.exe").
By leveraging Python's
psutilmodule, you can check either theread_countorread_bytesproperties of csrss.exe. These values should change any time there is input from the user, i.e. keystrokes or mouse events.First, you need to get the process ID (PID) for the csrss.exe process:
Once you have your csrss.exe PID(s), you can use
psutil'sio_countersmethod to get theread_countorread_bytesinfo (both will update any time there's user input - I useread_bytesbelow)The
get_iofunction will return a list of integers corresponding to theread_bytesvalues for each of the given csrss.exe process IDs. To check for user activity, this list should be periodically compared to a previously stored value - any changes mean there's been input from the user!Here's a quick demo:
To incorporate these functions into your service, simply include them in your main class (the one subclassing
ServiceFramework) - don't forget to add theselfparameter!You'll want to call
get_csrss_pidsand set the initial value oflast_ioat__init__and go from there: