Using Remote Desktop Services API
We can call WTSEnumerateProcesses function to get information about the active processes on a specified Remote Desktop Session Host server. However, if pass WTS_CURRENT_SERVER_HANDLE in first argument (server handle), we can enumerate and get info about processes which are running on local machine.
The following example calls WTSEnumerateProcesses function, then fills an array of WTS_PROCESS_INFO structures.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Windows Terminal Server API header & lib #include <Wtsapi32.h> #pragma comment(lib, "Wtsapi32.lib") DWORD RDSAPI_EnumProcesses(CArray<WTS_PROCESS_INFO>& arrProcInfo) { // clear output array arrProcInfo.RemoveAll(); HANDLE hServer = WTS_CURRENT_SERVER_HANDLE; // local machine processes PWTS_PROCESS_INFO pProcessInfo = NULL; DWORD dwCount = 0; // enumerate processes if(!::WTSEnumerateProcesses(hServer, 0, 1, &pProcessInfo, &dwCount)) { return ::GetLastError(); } // fill output array arrProcInfo.SetSize(dwCount); for(DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++) { arrProcInfo[dwIndex] = pProcessInfo[dwIndex]; } // free the memory allocated in WTSEnumerateProcesses ::WTSFreeMemory(pProcessInfo); return NO_ERROR; } |
If the target OS is Windows Vista or newer, we can alternatively use WTSEnumerateProcessesEx which can get additional info in WTS_PROCESS_INFO_EX structures.
Here is an example that enumerates processes, then fills an array of application-defined structures, (CProcessInfoEx) which contain the following info:
- the process identifier;
- the identifier of session associated with the process;
- the name of the executable file associated with the process;
- the user account name;
- the user domain name;
- the number of threads in the process;
- the number of handles in the process;
- the page file usage of the process, in bytes;
- the peak page file usage of the process, in bytes;
- the working set size of the process, in bytes;
- the peak working set size of the process, in bytes;
- the time, in milliseconds, the process has been running in user mode;
- the time, in milliseconds, the process has been running in kernel mode.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
DWORD RDSAPI_EnumProcessesEx(CArray<CProcessInfoEx>& arrProcInfo) { // clear output array arrProcInfo.RemoveAll(); // init WTSEnumerateProcessesEx parameters HANDLE hServer = WTS_CURRENT_SERVER_HANDLE; // get local machine processes DWORD dwLevel = 1; // get array of WTS_PROCESS_INFO_EX DWORD dwSessionID = WTS_ANY_SESSION; // get processes in all sessions DWORD dwCount = 0; // returns the number of processes PWTS_PROCESS_INFO_EX pProcessInfo = NULL; // output data if(!::WTSEnumerateProcessesEx(hServer, &dwLevel, dwSessionID, (LPTSTR*)&pProcessInfo, &dwCount)) { return ::GetLastError(); } // fill output array arrProcInfo.SetSize(dwCount); for(DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++) { CProcessInfoEx processInfoEx(pProcessInfo[dwIndex]); arrProcInfo[dwIndex] = processInfoEx; } // free the memory allocated in WTSEnumerateProcessesEx if(!::WTSFreeMemoryEx(WTSTypeProcessInfoLevel1, pProcessInfo, dwCount)) { return ::GetLastError(); } return NO_ERROR; } |
Now, let’s fill a listview control with the processes info grabbed by RDSAPI_EnumProcessesEx.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
void CDemoDlg::_FillProcessesList() { // clear listview control m_listProcesses.DeleteAllItems(); // get an array of CProcessInfoEx CArray<CProcessInfoEx> arrProcInfo; VERIFY(NO_ERROR == RDSAPI_EnumProcessesEx(arrProcInfo)); // fill the listview control const INT_PTR nSize = arrProcInfo.GetSize(); for(INT_PTR nItem = 0; nItem < nSize; nItem++) { // add new item to listview control m_listProcesses.InsertItem(nItem, NULL); // set list imtem text const CProcessInfoEx& processInfoEx = arrProcInfo.ElementAt(nItem); m_listProcesses.SetItemText(nItem, ePID, processInfoEx.GetProcessIdStr()); m_listProcesses.SetItemText(nItem, eSessionID, processInfoEx.GetSessionIdStr()); m_listProcesses.SetItemText(nItem, eImageName, processInfoEx.GetProcessNameStr()); m_listProcesses.SetItemText(nItem, eUserName, processInfoEx.GetUserNameStr()); m_listProcesses.SetItemText(nItem, eDomain, processInfoEx.GetDomainNameStr()); m_listProcesses.SetItemText(nItem, eThreads, processInfoEx.GetNumberOfThreadsStr()); m_listProcesses.SetItemText(nItem, eHandles, processInfoEx.GetHandleCountStr()); m_listProcesses.SetItemText(nItem, ePagefileUsage, processInfoEx.GetPagefileUsageStr()); m_listProcesses.SetItemText(nItem, ePeakPagefileUsage, processInfoEx.GetPeakPagefileUsageStr()); m_listProcesses.SetItemText(nItem, eWorkingSetSize, processInfoEx.GetWorkingSetSizeStr()); m_listProcesses.SetItemText(nItem, ePeakWorkingSetSize, processInfoEx.GetPeakWorkingSetSizeStr()); m_listProcesses.SetItemText(nItem, eUserTime, processInfoEx.GetUserTimeStr()); m_listProcesses.SetItemText(nItem, eKernelTime, processInfoEx.GetKernelTimeStr()); } } |
More implementation details can be found in the attached demo project.
Notes
- WTSEnumerateProcesses function requires at least Windows XP or Windows Server 2003;
- WTSEnumerateProcessesEx function requires at least Windows 7 or Windows Server 2008 R2;
- WTS prefix comes from Windows Terminal Services which is the former name of Remote Desktop Services.
Demo Project
The demo project is a simple MFC dialog-based application that uses the above functions.
Download: Listing_Processes_Using_RDS_API.zip (1432)
References
See also
- Codexpert blog: Listing Processes – Part 1: Introduction
- Codexpert blog: Listing Processes – Part 2: Using PSAPI
- Codexpert blog: Listing Processes – Part 3: Using Tool Help Library