Enumerating Terminal Sessions and listing CPU usage
12 Dec 2017
Recently, I was interested in enumerating all the sessions that are active on a terminal server and figuring out which sessions used the least CPU. Enumerating TS sessions is relatively easy to do and there are a couple of ways to do it.
You can use the WTSEnumerateSessionsEx API, to get back an array of WTS_SESSION_INFO_1 structures. The advantage of using WTS API is that you can open a handle to a remote machine and enumerate sessions on that machine.
The second method is to use LsaEnumerateLogonSessions() API to get the logon session LUIDs and then use LsaGetLogonSessionData() API to get details of each session. We’ll do this on another day.
Once you have enumerated the sessions and you can make a WMI query to “Win32_PerfFormattedData_TermService_TerminalServicesSession” class to retrieve performance data from ther Terminal Service provider. This may seem complicated, but once you get the hang of it - its easy. I have a code sample for this here: https://github.com/VimalShekar/Cpp/blob/master/src/termsessioncpu/terminalsessionlist.cpp
Here’s a quick walkthrough:
The first step is to initialize the COM infrastructure.
Next, create an instance of the Wbem locator and connect to the “root\cimv2” WMI namespace. If we were connecting to a remote machine, the namespace name would be “\\\\root\\cimv2"
Create an instance of WbemRefresher class. This class helps us retrieve performance data that is periodically refreshed. Use QueryInterface method to retrieve its IWbemConfigureRefresher interface. We will use this interface to configure the WbemRefresher.
Add an Enumerator object for the namespace and class that we are interested in. In this case “Win32_PerfFormattedData_TermService_TerminalServicesSession” class. The AddEnum() method of the IWbemConfigureRefresher returns a pointer to the IWbemHiPerfEnum.
Now we can call the pRefresher->Refresh(0L) method to get refreshed data any number of times. Use the enumerator to Get the value of the objects.
GetObjects() returns an array of objects. Each object in the array has attributes, first you a handle to the attribute’s location (this is sort of like getting an offset into where the attribute is within the object).
Now that you have handles to all the attributes you are interested in, you can iterate through the object to retrieve the attributes you are interested in and print them.
When it comes to cleaning up - again remember that GetObjects() returns an array of COM objects. So you have to Release() each object in the array by iterating over them, otherwise you would be leaking memory.
You can loop through steps 5,6,7 and 8 any number of times, until you have all the samples you need to deterministically tell which sessions are idle. Finally clean up all the COM objects you have instantiated.
Refer to the full sample on https://github.com/VimalShekar/Cpp/blob/master/src/termsessioncpu/terminalsessionlist.cpp