<< Hide left pane
Leave feedback
Processes and Threads Sample
This sample demonstrates how to list all processes and their
threads on Windows NT using the undocumented
ZwQuerySystemInformation function. This function, which
prototype is shown below, provides access to a wide variety of
system information.
NTSTATUS ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
The first parameter, SystemInformationClass, specifies
the type of information to retrieve. To obtain information about
processes and threads, this parameter should be set to 5.
The second parameter, SystemInformation, should point
to a buffer into which the system will store the requested information.
For processes and threads, the information is returned as an array
of SYSTEM_PROCESS_INFORMATION structures, one structure for
each process in the system. The SYSTEM_PROCESS_INFORMATION
structure contains most of the information that is displayed by
the Windows NT Task Manager and also includes an array of
SYSTEM_THREAD_INFORMATION structures that describe threads
of the process.
typedef struct _SYSTEM_THREAD_INFORMATION {
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitchCount;
LONG State;
LONG WaitReason;
} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryDelta;
ULONG ThreadCount;
ULONG Reserved1[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
#if _WIN32_WINNT >= 0x500
IO_COUNTERS IoCounters;
#endif
SYSTEM_THREAD_INFORMATION Threads[1];
} SYSTEM_PROCESS_INFORMATION, * SYSTEM_PROCESS_INFORMATION;
Note that the size of the SYSTEM_PROCESS_INFORMATION structure
differs between Windows NT 4.0 and Windows 2000. This is a perfect example
why you should not use undocumented functions.
Below is the source code of the sample application. The application
detemines the operating system version to handle correctly differences
in the process information structure. Then the application requests
processes and threads information and displays it on the screen.
int _tmain(
int argc,
_TCHAR * argv[]
)
{
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
ULONG cbBuffer = 0x8000;
LPVOID pBuffer = NULL;
NTSTATUS Status;
do
{
pBuffer = malloc(cbBuffer);
if (pBuffer == NULL)
{
_tprintf(_T("Not enough memory\n"));
return 1;
}
Status = ZwQuerySystemInformation(
SystemProcessesAndThreadsInformation,
pBuffer, cbBuffer, NULL);
if (Status == STATUS_INFO_LENGTH_MISMATCH)
{
free(pBuffer);
cbBuffer *= 2;
}
else if (!NT_SUCCESS(Status))
{
_tprintf(_T("ZwQuerySystemInformation failed with")
_T("status 0x%08X\n"), Status);
free(pBuffer);
return 1;
}
}
while (Status == STATUS_INFO_LENGTH_MISMATCH);
PSYSTEM_PROCESS_INFORMATION pInfo =
(PSYSTEM_PROCESS_INFORMATION)pBuffer;
for (;;)
{
PCWSTR pszProcessName = pInfo->ProcessName.Buffer;
if (pszProcessName == NULL)
pszProcessName = L"Idle";
_tprintf(_T("ProcessID: %d (%ls)\n"), pInfo->ProcessId,
pszProcessName);
ULONG ThreadCount = pInfo->ThreadCount;
PSYSTEM_THREAD_INFORMATION pThreads;
if (osvi.dwMajorVersion < 5)
pThreads = ((PSYSTEM_PROCESS_INFORMATION_NT4)pInfo)->Threads;
else
pThreads = pInfo->Threads;
for (ULONG i = 0; i < ThreadCount; i++)
{
DWORD dwThreadId = pThreads[i].ClientId.UniqueThread;
_tprintf(_T("\tThreadID: %d\n"), dwThreadId);
TestOpenThread(dwThreadId);
}
if (pInfo->NextEntryDelta == 0)
break;
pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo)
+ pInfo->NextEntryDelta);
}
free(pBuffer);
return 0;
}
Note: The method described is not the only way how the list of
processes and threads can be obtained. Other methods include performance
data interface, PSAPI (for the list of processes), Toolhelp32 API (available
in Windows 2000 and later) and Windows Management Instrumentation (WMI).
It is strongly recommented to use one of documented methods to obtain
this information rather than call ZwQuerySystemInformation.
Among other enhancements, a new Win32 API function named OpenThread
was added in Windows 2000. This function returns a handle of an existing thread
given the thread identifier. This sample shows how OpenThread can be
implemented on earlier versions of Windows NT using the undocumented
ZwOpenThread function.
Below is the source code of the OpenThread replacement. I won't
be surprised if the actual OpenThread code doesn't differ much
from what you see here.
HANDLE OpenThread(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwThreadId
)
{
OBJECT_ATTRIBUTES ObjectAttributes;
CLIENT_ID ClientId;
HANDLE hThread;
NTSTATUS Status;
memset(&ObjectAttributes, 0, sizeof(ObjectAttributes));
if (bInheritHandle)
ObjectAttributes.Attributes = OBJ_INHERIT;
ClientId.UniqueProcess = NULL;
ClientId.UniqueThread = (HANDLE)dwThreadId;
Status = ZwOpenThread(&hThread, dwDesiredAccess,
&ObjectAttributes, &ClientId);
if (!NT_SUCCESS(Status))
{
SetLastError(LsaNtStatusToWinError(Status));
return NULL;
}
return hThread;
}
References
- Gary Nebbett, Windows NT/2000 Native API Reference.
New Riders Publishing, 2000.