Search code examples
windowswinapilanguage-agnosticwindows-services

How to get the current number of interactive user sessions in Windows?


I'm writing a Windows service that needs to know whether there are any users currently logged-on in the machine.

So far I've tried Win32_LogonSession (WMI), and LsaEnumerateLogonSessions/LsaGetLogonSessionData (secur32.dll).

Both work, and seem to return the same data, but they are too slow to update when a user log off:

  • When the system starts, they return "0 interactive users". (OK)
  • When I log on, they return "1 interactive user". (OK)
  • But then when I log off, the number of users is kept at 1. After a new log-on, the number is 2, and so on.

Thus Win32_LogonSession nor LsaEnumerateLogonSessions are good enough. The service needs to know within 5 minutes after the last interactive user leaves.

Not even SysInternals' LogonSessions.exe gives up-to-date answers.
Also, the answer cannot be "monitor logon and logoff events and have a counter variable", because the service can be started at any time.


Solution

  • I ended up with the following approach: count the number of interactive sessions which have at least one process running.

    1) Get the logon session id for each interactive session.

    • LsaEnumerateLogonSessions (secur32.dll)
    • LsaGetLogonSessionData (secur32.dll)
    • sessionData.LogonType = SECURITY_LOGON_TYPE.Interactive or sessionData.LogonType = SECURITY_LOGON_TYPE.RemoteInteractive
    • sessionData.LoginID <- Keep this value in a LUID set.
    • LsaFreeReturnBuffer (secur32.dll)

    2) Get the logon session id for each running process.

    [First we need to enable the SeDebugPrivilege to the current application.]

    • GetCurrentProcess (kernel32.dll)
    • OpenProcessToken TOKEN_ADJUST_PRIVILEGES (advapi32.dll)
    • LookupPrivilegeValue SE_DEBUG_NAME (advapi32.dll)
    • AdjustTokenPrivileges (advapi32.dll)
    • CloseHandle (kernel32.dll)

    [Then retrieve the data we want.]

    3) Sets intersection cardinality

    interactiveSessionsCount = | { sessionData.LoginID } ∩ { accessTokenStatistics.AuthenticationId } |
    

    Obs: sessionData.LoginID and accessTokenStatistics.AuthenticationId are both of type LUID.