1 /* Console Task Manager
3 ctm.c - main program file
5 Written by: Aleksey Bragin (aleksey@studiocerebral.com)
7 Most of the code dealing with getting system parameters is taken from
8 ReactOS Task Manager written by Brian Palmer (brianp@reactos.org)
11 09 April 2003 - v0.1, fixed bugs, added features, ported to mingw
12 20 March 2003 - v0.03, works good under ReactOS, and allows process
14 18 March 2003 - Initial version 0.01, doesn't work under RectOS
16 This program is free software; you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation; either version 2 of the License, or
19 (at your option) any later version.
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
31 //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows //headers
40 #include <ddk/ntddk.h>
42 #include <ntos/zwtypes.h>
55 //PROCNTQSI NtQuerySystemInformation= NULL;
57 const int ProcPerScreen
= 17; // 17 processess are displayed on one page
58 ULONG ProcessCountOld
= 0;
59 ULONG ProcessCount
= 0;
64 LARGE_INTEGER liOldIdleTime
= {{0,0}};
65 double OldKernelTime
= 0;
66 LARGE_INTEGER liOldSystemTime
= {{0,0}};
68 PPERFDATA pPerfDataOld
= NULL
; // Older perf data (saved to establish delta values)
69 PPERFDATA pPerfData
= NULL
; // Most recent copy of perf data
72 int scrolled
=0; // offset from which process start showing
76 void *PsaiMalloc(SIZE_T size
) { return malloc(size
); }
77 void *PsaiRealloc(void *ptr
, SIZE_T size
) { return realloc(ptr
, size
); }
78 void PsaiFree(void *ptr
) { free(ptr
); }
81 unsigned int GetKeyPressed();
83 void GetInputOutputHandles()
86 HANDLE console
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
87 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
88 0, CONSOLE_TEXTMODE_BUFFER
, 0);
90 if (SetConsoleActiveScreenBuffer(console
) == FALSE
)
92 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
93 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
97 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);//console;
101 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
102 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
106 void RestoreConsole()
108 SetConsoleMode(hStdin
, inConMode
);
109 SetConsoleMode(hStdout
, outConMode
);
112 SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE
));
124 static int first
= 0;
129 pos
.X
= 2; pos
.Y
= 2;
130 _tcscpy(lpStr
, _T("Console TaskManager v0.1 by Aleksey Bragin <aleksey@studiocerebral.com>"));
131 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
133 pos
.X
= 2; pos
.Y
= 3;
134 _tcscpy(lpStr
, _T("+-------------------------------+-------+-----+-----------+-------------+"));
135 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
137 pos
.X
= 2; pos
.Y
= 4;
138 _tcscpy(lpStr
, _T("| Image name | PID | CPU | Mem Usage | Page Faults |"));
139 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
141 pos
.X
= 2; pos
.Y
= 5;
142 _tcscpy(lpStr
, _T("+-------------------------------+-------+-----+-----------+-------------+"));
143 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
146 pos
.X
= 2; pos
.Y
= 23;
147 _tcscpy(lpStr
, _T("+-------------------------------+-------+-----+-----------+-------------+"));
148 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
151 pos
.X
= 2; pos
.Y
= 24;
152 _tcscpy(lpStr
, _T("Press: q - quit, k - kill process "));
153 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
159 lines
= ProcessCount
;
160 if (lines
> MAX_PROC
)
162 for (idx
=0; idx
<MAX_PROC
; idx
++)
165 TCHAR imgName
[MAX_PATH
];
169 TCHAR lpPageFaults
[15];
174 if (idx
< lines
&& scrolled
+ idx
< ProcessCount
)
177 len
= wcslen(pPerfData
[scrolled
+idx
].ImageName
);
179 WideCharToMultiByte(CP_ACP
, 0, pPerfData
[scrolled
+idx
].ImageName
, -1,
180 imgName
, MAX_PATH
, NULL
, NULL
);
181 len
= strlen(imgName
);
188 wcsncpy(&lpStr
[2], pPerfData
[scrolled
+idx
].ImageName
, len
);
190 strncpy(&lpStr
[2], imgName
, len
);
199 _tcsncpy(&lpStr
[2 + len
], _T(" "), 31 - len
);
203 if (idx
< lines
&& scrolled
+ idx
< ProcessCount
)
205 _stprintf(lpPid
, _T("%6ld "), pPerfData
[scrolled
+idx
].ProcessId
);
206 _tcsncpy(&lpStr
[34], lpPid
, 7);
210 _tcsncpy(&lpStr
[34], _T(" "), 7);
214 if (idx
< lines
&& scrolled
+ idx
< ProcessCount
)
216 _stprintf(lpCpu
, _T("%3d%% "), pPerfData
[scrolled
+idx
].CPUUsage
);
217 _tcsncpy(&lpStr
[42], lpCpu
, 5);
221 _tcsncpy(&lpStr
[42], _T(" "), 5);
225 if (idx
< lines
&& scrolled
+ idx
< ProcessCount
)
227 _stprintf(lpMemUsg
, _T("%6ld "), pPerfData
[scrolled
+idx
].WorkingSetSizeBytes
/ 1024);
228 _tcsncpy(&lpStr
[48], lpMemUsg
, 11);
232 _tcsncpy(&lpStr
[48], _T(" "), 11);
236 if (idx
< lines
&& scrolled
+ idx
< ProcessCount
)
238 _stprintf(lpPageFaults
, _T("%12ld "), pPerfData
[scrolled
+idx
].PageFaultCount
);
239 _tcsncpy(&lpStr
[60], lpPageFaults
, 13);
243 _tcsncpy(&lpStr
[60], _T(" "), 13);
254 pos
.X
= 1; pos
.Y
= 6+idx
;
255 WriteConsoleOutputCharacter(hStdout
, lpStr
, 74, pos
, &numChars
);
258 pos
.X
= 3; pos
.Y
= 6+idx
;
259 if (selection
== idx
)
261 wColor
= BACKGROUND_GREEN
|
268 wColor
= BACKGROUND_BLUE
|
274 FillConsoleOutputAttribute(
275 hStdout
, // screen buffer handle
276 wColor
, // color to fill with
277 31, // number of cells to fill
278 pos
, // first cell to write to
279 &numChars
); // actual number written
285 // returns TRUE if exiting
286 int ProcessKeys(int numEvents
)
288 if ((ProcessCount
-scrolled
< 17) && (ProcessCount
> 17))
289 scrolled
= ProcessCount
-17;
291 TCHAR key
= GetKeyPressed(numEvents
);
294 else if (key
== VK_K
)
296 // user wants to kill some process, get his acknowledgement
301 pos
.X
= 2; pos
.Y
= 24;
302 _tcscpy(lpStr
, _T("Are you sure you want to kill this process? (y/n)"));
303 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &pId
);
306 GetNumberOfConsoleInputEvents(hStdin
, &pId
);
307 key
= GetKeyPressed(pId
);
313 pId
= pPerfData
[selection
+scrolled
].ProcessId
;
314 hProcess
= OpenProcess(PROCESS_TERMINATE
, FALSE
, pId
);
318 if (!TerminateProcess(hProcess
, 0))
320 _tcscpy(lpStr
, _T("Unable to terminate this process... "));
321 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &pId
);
325 CloseHandle(hProcess
);
329 _stprintf(lpStr
, _T("Unable to terminate process %3d (unable to OpenProcess) "), pId
);
330 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &pId
);
335 else if (key
== VK_UP
)
339 else if ((selection
== 0) && (scrolled
> 0))
342 else if (key
== VK_DOWN
)
344 if ((selection
< MAX_PROC
-1) && (selection
< ProcessCount
-1))
346 else if ((selection
== MAX_PROC
-1) && (selection
+scrolled
< ProcessCount
-1))
355 // NtQuerySystemInformation = //(PROCNTQSI)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), //"NtQuerySystemInformation");
358 void PerfDataRefresh()
366 HANDLE hProcessToken
;
367 PSYSTEM_PROCESSES pSPI
;
369 TCHAR szTemp
[MAX_PATH
];
371 double CurrentKernelTime
;
372 PSYSTEM_PROCESSORTIME_INFO SysProcessorTimeInfo
;
373 SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo
;
374 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo
;
377 // Get new system time
378 status
= NtQuerySystemInformation(SystemTimeInformation
, &SysTimeInfo
, sizeof(SysTimeInfo
), 0);
379 if (status
!= NO_ERROR
)
382 // Get new CPU's idle time
383 status
= NtQuerySystemInformation(SystemPerformanceInformation
, &SysPerfInfo
, sizeof(SysPerfInfo
), NULL
);
384 if (status
!= NO_ERROR
)
387 // Get processor information
388 SysProcessorTimeInfo
= (PSYSTEM_PROCESSORTIME_INFO
)malloc(sizeof(SYSTEM_PROCESSORTIME_INFO
) * 1/*SystemBasicInfo.bKeNumberProcessors*/);
389 status
= NtQuerySystemInformation(SystemProcessorTimes
, SysProcessorTimeInfo
, sizeof(SYSTEM_PROCESSORTIME_INFO
) * 1/*SystemBasicInfo.bKeNumberProcessors*/, &ulSize
);
392 // Get process information
393 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESSES
*)&pBuffer
);
396 for (CurrentKernelTime
=0, Idx
=0; Idx
<1/*SystemBasicInfo.bKeNumberProcessors*/; Idx
++) {
397 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].TotalProcessorTime
);
398 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].TotalDPCTime
);
399 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].TotalInterruptTime
);
402 // If it's a first call - skip idle time calcs
403 if (liOldIdleTime
.QuadPart
!= 0) {
404 // CurrentValue = NewValue - OldValue
405 dbIdleTime
= Li2Double(SysPerfInfo
.IdleTime
) - Li2Double(liOldIdleTime
);
406 dbKernelTime
= CurrentKernelTime
- OldKernelTime
;
407 dbSystemTime
= Li2Double(SysTimeInfo
.CurrentTime
) - Li2Double(liOldSystemTime
);
409 // CurrentCpuIdle = IdleTime / SystemTime
410 dbIdleTime
= dbIdleTime
/ dbSystemTime
;
411 dbKernelTime
= dbKernelTime
/ dbSystemTime
;
413 // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
414 dbIdleTime
= 100.0 - dbIdleTime
* 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5; */
415 dbKernelTime
= 100.0 - dbKernelTime
* 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5; */
418 // Store new CPU's idle and system time
419 liOldIdleTime
= SysPerfInfo
.IdleTime
;
420 liOldSystemTime
= SysTimeInfo
.CurrentTime
;
421 OldKernelTime
= CurrentKernelTime
;
424 // Determine the process count
425 // We loop through the data we got from PsaCaptureProcessesAndThreads
426 // and count how many structures there are (until PsaWalkNextProcess
428 ProcessCountOld
= ProcessCount
;
430 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESSES
)pBuffer
);
433 pSPI
= PsaWalkNextProcess(pSPI
);
436 // Now alloc a new PERFDATA array and fill in the data
438 //delete[] pPerfDataOld;
441 pPerfDataOld
= pPerfData
;
442 //pPerfData = new PERFDATA[ProcessCount];
443 pPerfData
= (PPERFDATA
)malloc(sizeof(PERFDATA
) * ProcessCount
);
444 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESSES
)pBuffer
);
445 for (Idx
=0; Idx
<ProcessCount
; Idx
++) {
446 // Get the old perf data for this process (if any)
447 // so that we can establish delta values
449 for (Idx2
=0; Idx2
<ProcessCountOld
; Idx2
++) {
450 if (pPerfDataOld
[Idx2
].ProcessId
== pSPI
->ProcessId
) {
451 pPDOld
= &pPerfDataOld
[Idx2
];
456 // Clear out process perf data structure
457 memset(&pPerfData
[Idx
], 0, sizeof(PERFDATA
));
459 if (pSPI
->ProcessName
.Buffer
) {
460 wcsncpy(pPerfData
[Idx
].ImageName
, pSPI
->ProcessName
.Buffer
, pSPI
->ProcessName
.Length
/ sizeof(WCHAR
));
461 pPerfData
[Idx
].ImageName
[pSPI
->ProcessName
.Length
/ sizeof(WCHAR
)] = 0;
464 wcscpy(pPerfData
[Idx
].ImageName
, L
"System Idle Process");
466 pPerfData
[Idx
].ProcessId
= pSPI
->ProcessId
;
470 double CurTime
= Li2Double(pSPI
->KernelTime
) + Li2Double(pSPI
->UserTime
);
471 double OldTime
= Li2Double(pPDOld
->KernelTime
) + Li2Double(pPDOld
->UserTime
);
472 double CpuTime
= (CurTime
- OldTime
) / dbSystemTime
;
473 CpuTime
= CpuTime
* 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5;*/
475 pPerfData
[Idx
].CPUUsage
= (ULONG
)CpuTime
;
477 pPerfData
[Idx
].CPUUsage
= 0;
481 pPerfData
[Idx
].CPUTime
.QuadPart
= pSPI
->UserTime
.QuadPart
+ pSPI
->KernelTime
.QuadPart
;
482 pPerfData
[Idx
].WorkingSetSizeBytes
= pSPI
->VmCounters
.WorkingSetSize
;
483 pPerfData
[Idx
].PeakWorkingSetSizeBytes
= pSPI
->VmCounters
.PeakWorkingSetSize
;
485 pPerfData
[Idx
].WorkingSetSizeDelta
= labs((LONG
)pSPI
->VmCounters
.WorkingSetSize
- (LONG
)pPDOld
->WorkingSetSizeBytes
);
487 pPerfData
[Idx
].WorkingSetSizeDelta
= 0;
488 pPerfData
[Idx
].PageFaultCount
= pSPI
->VmCounters
.PageFaultCount
;
490 pPerfData
[Idx
].PageFaultCountDelta
= labs((LONG
)pSPI
->VmCounters
.PageFaultCount
- (LONG
)pPDOld
->PageFaultCount
);
492 pPerfData
[Idx
].PageFaultCountDelta
= 0;
493 pPerfData
[Idx
].VirtualMemorySizeBytes
= pSPI
->VmCounters
.VirtualSize
;
494 pPerfData
[Idx
].PagedPoolUsagePages
= pSPI
->VmCounters
.QuotaPagedPoolUsage
;
495 pPerfData
[Idx
].NonPagedPoolUsagePages
= pSPI
->VmCounters
.QuotaNonPagedPoolUsage
;
496 pPerfData
[Idx
].BasePriority
= pSPI
->BasePriority
;
497 pPerfData
[Idx
].HandleCount
= pSPI
->HandleCount
;
498 pPerfData
[Idx
].ThreadCount
= pSPI
->ThreadCount
;
499 //pPerfData[Idx].SessionId = pSPI->SessionId;
502 hProcess
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, pSPI
->ProcessId
);
504 if (OpenProcessToken(hProcess
, TOKEN_QUERY
|TOKEN_DUPLICATE
|TOKEN_IMPERSONATE
, &hProcessToken
)) {
505 ImpersonateLoggedOnUser(hProcessToken
);
506 memset(szTemp
, 0, sizeof(TCHAR
[MAX_PATH
]));
508 GetUserName(szTemp
, &dwSize
);
510 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, szTemp
, -1, pPerfData
[Idx
].UserName
, MAX_PATH
);
512 int MultiByteToWideChar(
513 UINT CodePage, // code page
514 DWORD dwFlags, // character-type options
515 LPCSTR lpMultiByteStr, // string to map
516 int cbMultiByte, // number of bytes in string
517 LPWSTR lpWideCharStr, // wide-character buffer
518 int cchWideChar // size of buffer
523 CloseHandle(hProcessToken
);
525 CloseHandle(hProcess
);
529 pPerfData
[Idx
].UserTime
.QuadPart
= pSPI
->UserTime
.QuadPart
;
530 pPerfData
[Idx
].KernelTime
.QuadPart
= pSPI
->KernelTime
.QuadPart
;
532 pSPI
= PsaWalkNextProcess(pSPI
);
535 PsaFreeCapture(pBuffer
);
537 free(SysProcessorTimeInfo
);
540 // Code partly taken from slw32tty.c from mc/slang
541 unsigned int GetKeyPressed(int events
)
549 for (i
=0; i
<events
; i
++)
551 if (!ReadConsoleInput(hStdin
, &record
, 0, &bytesRead
)) {
554 if (!ReadConsoleInput(hStdin
, &record
, 1, &bytesRead
)) {
558 if (record
.EventType
== KEY_EVENT
&& record
.Event
.KeyEvent
.bKeyDown
)
559 return record
.Event
.KeyEvent
.wVirtualKeyCode
;//.uChar.AsciiChar;
566 int main(int *argc
, char **argv
)
568 GetInputOutputHandles();
570 if (hStdin
== INVALID_HANDLE_VALUE
|| hStdout
== INVALID_HANDLE_VALUE
)
572 printf("ctm: can't use console.");
576 if (GetConsoleMode(hStdin
, &inConMode
) == 0)
578 printf("ctm: can't GetConsoleMode() for input console.");
582 if (GetConsoleMode(hStdout
, &outConMode
) == 0)
584 printf("ctm: can't GetConsoleMode() for output console.");
588 SetConsoleMode(hStdin
, 0); //FIXME: Should check for error!
589 SetConsoleMode(hStdout
, 0); //FIXME: Should check for error!
600 //WriteConsole(hStdin, " ", 1, &numEvents, NULL); // TODO: Make another way (this is ugly, I know)
602 /* WaitForSingleObject for console handles is not implemented in ROS */
603 WaitForSingleObject(hStdin
, 1000);
606 GetNumberOfConsoleInputEvents(hStdin
, &numEvents
);
610 if (ProcessKeys(numEvents
) == TRUE
)
616 /* Should be removed, if WaitForSingleObject is implemented for console handles */
617 Sleep(40); // TODO: Should be done more efficient (might be another thread handling input/etc)*/