mirror of
https://github.com/reactos/reactos.git
synced 2024-08-16 16:39:08 +00:00
336 lines
9.3 KiB
C
336 lines
9.3 KiB
C
/*
|
|
* PROJECT: ReactOS Tasklist Command
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Displays a list of currently running processes on the computer.
|
|
* COPYRIGHT: Copyright 2021 He Yang (1160386205@qq.com)
|
|
*/
|
|
|
|
#include "tasklist.h"
|
|
|
|
// the strings in OptionList are the command-line options.
|
|
// should always correspond with the defines below, in sequence (except OPTION_INVALID)
|
|
static PCWSTR OptionList[] = { L"?", L"nh" };
|
|
|
|
#define OPTION_INVALID -1
|
|
#define OPTION_HELP 0
|
|
#define OPTION_NOHEADER 1
|
|
|
|
// the max string length PrintResString can handle
|
|
#define RES_STR_MAXLEN 64
|
|
|
|
// Print split line
|
|
VOID PrintSplitLine(UINT Length)
|
|
{
|
|
for (; Length; Length--)
|
|
{
|
|
ConPuts(StdOut, L"=");
|
|
}
|
|
}
|
|
|
|
// Print spaces
|
|
VOID PrintSpace(UINT Length)
|
|
{
|
|
ConPrintf(StdOut, L"%*ls", (INT)Length, L"");
|
|
}
|
|
|
|
// Print a string.
|
|
// if bAlignLeft == TRUE then aligned to left, otherwise aligned to right
|
|
// MaxWidth is the width for printing.
|
|
VOID PrintString(LPCWSTR String, UINT MaxWidth, BOOL bAlignLeft)
|
|
{
|
|
ConPrintf(StdOut, bAlignLeft ? L"%-*.*ls" : L"%*.*ls", MaxWidth, MaxWidth, String);
|
|
}
|
|
|
|
// Print a string from resource
|
|
// if bAlignLeft == TRUE then aligned to left, otherwise aligned to right
|
|
// MaxWidth is the width for printing.
|
|
// The string WILL be truncated if it's longer than RES_STR_MAXLEN
|
|
VOID PrintResString(HINSTANCE hInstance, UINT uID, UINT MaxWidth, BOOL bAlignLeft)
|
|
{
|
|
if (!hInstance)
|
|
return;
|
|
|
|
WCHAR StringBuffer[RES_STR_MAXLEN];
|
|
LoadStringW(hInstance, uID, StringBuffer, _countof(StringBuffer));
|
|
PrintString(StringBuffer, MaxWidth, bAlignLeft);
|
|
}
|
|
|
|
// Print a number, aligned to right.
|
|
// MaxWidth is the width for printing.
|
|
// the number WILL NOT be truncated if it's longer than MaxWidth
|
|
VOID PrintNum(LONGLONG Number, UINT MaxWidth)
|
|
{
|
|
ConPrintf(StdOut, L"%*lld", MaxWidth, Number);
|
|
}
|
|
|
|
// Print memory size using KB as unit, with comma-separated number, aligned to right.
|
|
// MaxWidth is the width for printing.
|
|
// the number WILL be truncated if it's longer than MaxWidth
|
|
BOOL PrintMemory(SIZE_T MemorySizeByte, UINT MaxWidth, HINSTANCE hInstance)
|
|
{
|
|
if (!hInstance)
|
|
return FALSE;
|
|
|
|
SIZE_T MemorySize = MemorySizeByte >> 10;
|
|
|
|
WCHAR NumberString[27] = { 0 }; // length 26 is enough to display ULLONG_MAX in decimal with comma, one more for zero-terminated.
|
|
C_ASSERT(sizeof(SIZE_T) <= 8);
|
|
|
|
PWCHAR pNumberStr = NumberString;
|
|
|
|
// calculate the length
|
|
UINT PrintLength = 0;
|
|
SIZE_T Tmp = MemorySize;
|
|
UINT Mod = 1;
|
|
|
|
do
|
|
{
|
|
Tmp /= 10;
|
|
PrintLength++;
|
|
Mod *= 10;
|
|
} while (Tmp);
|
|
|
|
for (UINT i = PrintLength; i; i--)
|
|
{
|
|
Mod /= 10;
|
|
*pNumberStr = L'0' + (MemorySize / Mod);
|
|
MemorySize %= Mod;
|
|
pNumberStr++;
|
|
|
|
if (i != 1 && i % 3 == 1)
|
|
{
|
|
*pNumberStr = L',';
|
|
pNumberStr++;
|
|
}
|
|
}
|
|
|
|
WCHAR FormatStr[RES_STR_MAXLEN];
|
|
LoadStringW(hInstance, IDS_MEMORY_STR, FormatStr, _countof(FormatStr));
|
|
|
|
WCHAR String[RES_STR_MAXLEN + _countof(NumberString)] = { 0 };
|
|
|
|
StringCchPrintfW(String, _countof(String), FormatStr, NumberString);
|
|
PrintString(String, MaxWidth, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID PrintHeader(HINSTANCE hInstance)
|
|
{
|
|
if (!hInstance)
|
|
return;
|
|
|
|
PrintResString(hInstance, IDS_HEADER_IMAGENAME, COLUMNWIDTH_IMAGENAME, TRUE);
|
|
PrintSpace(1);
|
|
PrintResString(hInstance, IDS_HEADER_PID, COLUMNWIDTH_PID, FALSE);
|
|
PrintSpace(1);
|
|
PrintResString(hInstance, IDS_HEADER_SESSION, COLUMNWIDTH_SESSION, FALSE);
|
|
PrintSpace(1);
|
|
PrintResString(hInstance, IDS_HEADER_MEMUSAGE, COLUMNWIDTH_MEMUSAGE, FALSE);
|
|
|
|
ConPuts(StdOut, L"\n");
|
|
|
|
PrintSplitLine(COLUMNWIDTH_IMAGENAME);
|
|
PrintSpace(1);
|
|
PrintSplitLine(COLUMNWIDTH_PID);
|
|
PrintSpace(1);
|
|
PrintSplitLine(COLUMNWIDTH_SESSION);
|
|
PrintSpace(1);
|
|
PrintSplitLine(COLUMNWIDTH_MEMUSAGE);
|
|
|
|
ConPuts(StdOut, L"\n");
|
|
}
|
|
|
|
BOOL EnumProcessAndPrint(BOOL bNoHeader)
|
|
{
|
|
// Call NtQuerySystemInformation for the process information
|
|
ULONG ProcessInfoBufferLength = 0;
|
|
ULONG ResultLength = 0;
|
|
PBYTE ProcessInfoBuffer = NULL;
|
|
|
|
// Get the buffer size we need
|
|
NTSTATUS Status = NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &ResultLength);
|
|
|
|
// New process/thread might appear before we call for the actual data.
|
|
// Try to avoid this by retrying several times.
|
|
for (UINT Retry = 0; Retry < NT_SYSTEM_QUERY_MAX_RETRY; Retry++)
|
|
{
|
|
// (Re)allocate buffer
|
|
ProcessInfoBufferLength = ResultLength;
|
|
ResultLength = 0;
|
|
if (ProcessInfoBuffer)
|
|
{
|
|
PBYTE NewProcessInfoBuffer = HeapReAlloc(GetProcessHeap(), 0,
|
|
ProcessInfoBuffer,
|
|
ProcessInfoBufferLength);
|
|
if (NewProcessInfoBuffer)
|
|
{
|
|
ProcessInfoBuffer = NewProcessInfoBuffer;
|
|
}
|
|
else
|
|
{
|
|
// out of memory
|
|
ConResMsgPrintf(StdErr, 0, IDS_OUT_OF_MEMORY);
|
|
HeapFree(GetProcessHeap(), 0, ProcessInfoBuffer);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProcessInfoBuffer = HeapAlloc(GetProcessHeap(), 0, ProcessInfoBufferLength);
|
|
if (!ProcessInfoBuffer)
|
|
{
|
|
// out of memory
|
|
ConResMsgPrintf(StdErr, 0, IDS_OUT_OF_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Query information
|
|
Status = NtQuerySystemInformation(SystemProcessInformation,
|
|
ProcessInfoBuffer,
|
|
ProcessInfoBufferLength,
|
|
&ResultLength);
|
|
if (Status != STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
// tried NT_SYSTEM_QUERY_MAX_RETRY times, or failed with some other reason
|
|
ConResMsgPrintf(StdErr, 0, IDS_ENUM_FAILED);
|
|
HeapFree(GetProcessHeap(), 0, ProcessInfoBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
HINSTANCE hInstance = GetModuleHandleW(NULL);
|
|
assert(hInstance);
|
|
|
|
ConPuts(StdOut, L"\n");
|
|
|
|
if (!bNoHeader)
|
|
{
|
|
PrintHeader(hInstance);
|
|
}
|
|
|
|
PSYSTEM_PROCESS_INFORMATION pSPI;
|
|
pSPI = (PSYSTEM_PROCESS_INFORMATION)ProcessInfoBuffer;
|
|
while (pSPI)
|
|
{
|
|
PrintString(pSPI->UniqueProcessId ? pSPI->ImageName.Buffer : L"System Idle Process", COLUMNWIDTH_IMAGENAME, TRUE);
|
|
PrintSpace(1);
|
|
PrintNum((LONGLONG)(INT_PTR)pSPI->UniqueProcessId, COLUMNWIDTH_PID);
|
|
PrintSpace(1);
|
|
PrintNum((ULONGLONG)pSPI->SessionId, COLUMNWIDTH_SESSION);
|
|
PrintSpace(1);
|
|
PrintMemory(pSPI->WorkingSetSize, COLUMNWIDTH_MEMUSAGE, hInstance);
|
|
|
|
ConPuts(StdOut, L"\n");
|
|
|
|
if (pSPI->NextEntryOffset == 0)
|
|
break;
|
|
pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->NextEntryOffset);
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, ProcessInfoBuffer);
|
|
return TRUE;
|
|
}
|
|
|
|
INT GetOptionType(LPCWSTR szOption)
|
|
{
|
|
if (szOption[0] != L'/' && szOption[0] != L'-')
|
|
{
|
|
return OPTION_INVALID;
|
|
}
|
|
szOption++;
|
|
|
|
for (UINT i = 0; i < _countof(OptionList); i++)
|
|
{
|
|
if (!_wcsicmp(OptionList[i], szOption))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return OPTION_INVALID;
|
|
}
|
|
|
|
BOOL ProcessArguments(INT argc, WCHAR **argv)
|
|
{
|
|
BOOL bHasHelp = FALSE, bHasNoHeader = FALSE;
|
|
|
|
for (INT i = 1; i < argc; i++)
|
|
{
|
|
INT Option = GetOptionType(argv[i]);
|
|
|
|
switch (Option)
|
|
{
|
|
case OPTION_HELP:
|
|
{
|
|
if (bHasHelp)
|
|
{
|
|
// -? already specified
|
|
ConResMsgPrintf(StdErr, 0, IDS_OPTION_TOO_MUCH, argv[i], 1);
|
|
ConResMsgPrintf(StdErr, 0, IDS_USAGE);
|
|
return FALSE;
|
|
}
|
|
bHasHelp = TRUE;
|
|
break;
|
|
}
|
|
case OPTION_NOHEADER:
|
|
{
|
|
if (bHasNoHeader)
|
|
{
|
|
// -nh already specified
|
|
ConResMsgPrintf(StdErr, 0, IDS_OPTION_TOO_MUCH, argv[i], 1);
|
|
ConResMsgPrintf(StdErr, 0, IDS_USAGE);
|
|
return FALSE;
|
|
}
|
|
bHasNoHeader = TRUE;
|
|
break;
|
|
}
|
|
case OPTION_INVALID:
|
|
default:
|
|
{
|
|
ConResMsgPrintf(StdErr, 0, IDS_INVALID_OPTION);
|
|
ConResMsgPrintf(StdErr, 0, IDS_USAGE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bHasHelp)
|
|
{
|
|
if (argc > 2) // any arguments other than -? exists
|
|
{
|
|
ConResMsgPrintf(StdErr, 0, IDS_INVALID_SYNTAX);
|
|
ConResMsgPrintf(StdErr, 0, IDS_USAGE);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ConResMsgPrintf(StdOut, 0, IDS_USAGE);
|
|
ConResMsgPrintf(StdOut, 0, IDS_DESCRIPTION);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EnumProcessAndPrint(bHasNoHeader);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int wmain(int argc, WCHAR **argv)
|
|
{
|
|
/* Initialize the Console Standard Streams */
|
|
ConInitStdStreams();
|
|
|
|
if (!ProcessArguments(argc, argv))
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|