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>
54 //PROCNTQSI NtQuerySystemInformation= NULL;
56 const int ProcPerScreen
= 17; // 17 processess are displayed on one page
57 ULONG ProcessCountOld
= 0;
58 ULONG ProcessCount
= 0;
63 LARGE_INTEGER liOldIdleTime
= {{0,0}};
64 double OldKernelTime
= 0;
65 LARGE_INTEGER liOldSystemTime
= {{0,0}};
67 PPERFDATA pPerfDataOld
= NULL
; // Older perf data (saved to establish delta values)
68 PPERFDATA pPerfData
= NULL
; // Most recent copy of perf data
71 int scrolled
=0; // offset from which process start showing
75 void *PsaiMalloc(SIZE_T size
) { return malloc(size
); }
76 void *PsaiRealloc(void *ptr
, SIZE_T size
) { return realloc(ptr
, size
); }
77 void PsaiFree(void *ptr
) { free(ptr
); }
80 unsigned int GetKeyPressed();
82 void GetInputOutputHandles()
85 HANDLE console
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
86 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
87 0, CONSOLE_TEXTMODE_BUFFER
, 0);
89 if (SetConsoleActiveScreenBuffer(console
) == FALSE
)
91 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
92 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
96 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);//console;
100 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
101 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
105 void RestoreConsole()
107 SetConsoleMode(hStdin
, inConMode
);
108 SetConsoleMode(hStdout
, outConMode
);
111 SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE
));
124 pos
.X
= 2; pos
.Y
= 2;
125 strcpy(lpStr
, "Console TaskManager v0.1 by Aleksey Bragin <aleksey@studiocerebral.com>");
126 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &numChars
);
128 pos
.X
= 2; pos
.Y
= 3;
129 strcpy(lpStr
, "+-------------------------------+-------+-----+-----------+-------------+");
130 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &numChars
);
132 pos
.X
= 2; pos
.Y
= 4;
133 strcpy(lpStr
, "| Image name | PID | CPU | Mem Usage | Page Faults |");
134 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &numChars
);
136 pos
.X
= 2; pos
.Y
= 5;
137 strcpy(lpStr
, "+-------------------------------+-------+-----+-----------+-------------+");
138 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &numChars
);
141 pos
.X
= 2; pos
.Y
= 23;
142 strcpy(lpStr
, "+-------------------------------+-------+-----+-----------+-------------+");
143 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &numChars
);
146 pos
.X
= 2; pos
.Y
= 24;
147 strcpy(lpStr
, "Press: q - quit, k - kill process ");
148 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &numChars
);
151 lines
= ProcessCount
;
152 if (lines
> MAX_PROC
)
154 for (idx
=0; idx
<lines
; idx
++)
157 char imgName
[MAX_PATH
];
161 char lpPageFaults
[15];
166 pos
.X
= 3; pos
.Y
= 6+idx
;
167 memset(imgName
, 0, MAX_PATH
);
168 WideCharToMultiByte(CP_ACP
, 0, pPerfData
[scrolled
+idx
].ImageName
, -1,
169 imgName
, MAX_PATH
, NULL
, NULL
);
170 len
= strlen(imgName
);
171 WriteConsoleOutputCharacter(hStdout
, " ", 30, pos
, &numChars
);
172 WriteConsoleOutputCharacter(hStdout
, imgName
, (len
> 30) ? 30 : len
, pos
, &numChars
);
175 pos
.X
= 35; pos
.Y
= 6+idx
;
176 sprintf(lpPid
, "%6ld", pPerfData
[scrolled
+idx
].ProcessId
);
177 WriteConsoleOutputCharacter(hStdout
, lpPid
, strlen(lpPid
), pos
, &numChars
);
180 pos
.X
= 43; pos
.Y
= 6+idx
;
181 sprintf(lpCpu
, "%3d%%", pPerfData
[scrolled
+idx
].CPUUsage
);
182 WriteConsoleOutputCharacter(hStdout
, lpCpu
, strlen(lpCpu
), pos
, &numChars
);
185 pos
.X
= 49; pos
.Y
= 6+idx
;
186 sprintf(lpMemUsg
, "%6ld", pPerfData
[scrolled
+idx
].WorkingSetSizeBytes
/ 1024);
187 WriteConsoleOutputCharacter(hStdout
, lpMemUsg
, strlen(lpMemUsg
), pos
, &numChars
);
190 pos
.X
= 61; pos
.Y
= 6+idx
;
191 sprintf(lpPageFaults
, "%12ld", pPerfData
[scrolled
+idx
].PageFaultCount
);
192 WriteConsoleOutputCharacter(hStdout
, lpPageFaults
, strlen(lpPageFaults
), pos
, &numChars
);
195 pos
.X
= 2; pos
.Y
= 6+idx
;
196 WriteConsoleOutputCharacter(hStdout
, "|", 1, pos
, &numChars
);
197 pos
.X
= 34; pos
.Y
= 6+idx
;
198 WriteConsoleOutputCharacter(hStdout
, "|", 1, pos
, &numChars
);
199 pos
.X
= 42; pos
.Y
= 6+idx
;
200 WriteConsoleOutputCharacter(hStdout
, "|", 1, pos
, &numChars
);
201 pos
.X
= 48; pos
.Y
= 6+idx
;
202 WriteConsoleOutputCharacter(hStdout
, "|", 1, pos
, &numChars
);
203 pos
.X
= 60; pos
.Y
= 6+idx
;
204 WriteConsoleOutputCharacter(hStdout
, "|", 1, pos
, &numChars
);
205 pos
.X
= 74; pos
.Y
= 6+idx
;
206 WriteConsoleOutputCharacter(hStdout
, "|", 1, pos
, &numChars
);
210 pos
.X
= 3; pos
.Y
= 6+idx
;
211 if (selection
== idx
)
213 wColor
= BACKGROUND_GREEN
|
220 wColor
= BACKGROUND_BLUE
|
226 FillConsoleOutputAttribute(
227 hStdout
, // screen buffer handle
228 wColor
, // color to fill with
229 31, // number of cells to fill
230 pos
, // first cell to write to
231 &numChars
); // actual number written
237 // returns TRUE if exiting
238 int ProcessKeys(int numEvents
)
240 if ((ProcessCount
-scrolled
< 17) && (ProcessCount
> 17))
241 scrolled
= ProcessCount
-17;
243 unsigned char key
= GetKeyPressed(numEvents
);
246 else if (key
== VK_K
)
248 // user wants to kill some process, get his acknowledgement
253 pos
.X
= 2; pos
.Y
= 24;
254 strcpy(lpStr
, "Are you sure you want to kill this process? (y/n)");
255 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &pId
);
258 GetNumberOfConsoleInputEvents(hStdin
, &pId
);
259 key
= GetKeyPressed(pId
);
265 pId
= pPerfData
[selection
+scrolled
].ProcessId
;
266 hProcess
= OpenProcess(PROCESS_TERMINATE
, FALSE
, pId
);
270 if (!TerminateProcess(hProcess
, 0))
272 strcpy(lpStr
, "Unable to terminate this process... ");
273 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &pId
);
277 CloseHandle(hProcess
);
281 sprintf(lpStr
, "Unable to terminate process %3d (unable to OpenProcess) ", pId
);
282 WriteConsoleOutputCharacter(hStdout
, lpStr
, strlen(lpStr
), pos
, &pId
);
287 else if (key
== VK_UP
)
291 else if ((selection
== 0) && (scrolled
> 0))
294 else if (key
== VK_DOWN
)
296 if ((selection
< MAX_PROC
-1) && (selection
< ProcessCount
-1))
298 else if ((selection
== MAX_PROC
-1) && (selection
+scrolled
< ProcessCount
-1))
307 // NtQuerySystemInformation = //(PROCNTQSI)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), //"NtQuerySystemInformation");
310 void PerfDataRefresh()
318 HANDLE hProcessToken
;
319 PSYSTEM_PROCESSES pSPI
;
321 TCHAR szTemp
[MAX_PATH
];
323 double CurrentKernelTime
;
324 PSYSTEM_PROCESSORTIME_INFO SysProcessorTimeInfo
;
325 SYSTEM_PERFORMANCE_INFO SysPerfInfo
;
326 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo
;
329 // Get new system time
330 status
= NtQuerySystemInformation(SystemTimeInformation
, &SysTimeInfo
, sizeof(SysTimeInfo
), 0);
331 if (status
!= NO_ERROR
)
334 // Get new CPU's idle time
335 status
= NtQuerySystemInformation(SystemPerformanceInformation
, &SysPerfInfo
, sizeof(SysPerfInfo
), NULL
);
336 if (status
!= NO_ERROR
)
339 // Get processor information
340 SysProcessorTimeInfo
= (PSYSTEM_PROCESSORTIME_INFO
)malloc(sizeof(SYSTEM_PROCESSORTIME_INFO
) * 1/*SystemBasicInfo.bKeNumberProcessors*/);
341 status
= NtQuerySystemInformation(SystemProcessorTimes
, SysProcessorTimeInfo
, sizeof(SYSTEM_PROCESSORTIME_INFO
) * 1/*SystemBasicInfo.bKeNumberProcessors*/, &ulSize
);
344 // Get process information
345 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESSES
*)&pBuffer
);
348 for (CurrentKernelTime
=0, Idx
=0; Idx
<1/*SystemBasicInfo.bKeNumberProcessors*/; Idx
++) {
349 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].KernelTime
);
350 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].DpcTime
);
351 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].InterruptTime
);
354 // If it's a first call - skip idle time calcs
355 if (liOldIdleTime
.QuadPart
!= 0) {
356 // CurrentValue = NewValue - OldValue
357 dbIdleTime
= Li2Double(SysPerfInfo
.liIdleTime
) - Li2Double(liOldIdleTime
);
358 dbKernelTime
= CurrentKernelTime
- OldKernelTime
;
359 dbSystemTime
= Li2Double(SysTimeInfo
.CurrentTime
) - Li2Double(liOldSystemTime
);
361 // CurrentCpuIdle = IdleTime / SystemTime
362 dbIdleTime
= dbIdleTime
/ dbSystemTime
;
363 dbKernelTime
= dbKernelTime
/ dbSystemTime
;
365 // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
366 dbIdleTime
= 100.0 - dbIdleTime
* 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5; */
367 dbKernelTime
= 100.0 - dbKernelTime
* 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5; */
370 // Store new CPU's idle and system time
371 liOldIdleTime
= SysPerfInfo
.liIdleTime
;
372 liOldSystemTime
= SysTimeInfo
.CurrentTime
;
373 OldKernelTime
= CurrentKernelTime
;
376 // Determine the process count
377 // We loop through the data we got from PsaCaptureProcessesAndThreads
378 // and count how many structures there are (until PsaWalkNextProcess
380 ProcessCountOld
= ProcessCount
;
382 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESSES
)pBuffer
);
385 pSPI
= PsaWalkNextProcess(pSPI
);
388 // Now alloc a new PERFDATA array and fill in the data
390 //delete[] pPerfDataOld;
393 pPerfDataOld
= pPerfData
;
394 //pPerfData = new PERFDATA[ProcessCount];
395 pPerfData
= (PPERFDATA
)malloc(sizeof(PERFDATA
) * ProcessCount
);
396 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESSES
)pBuffer
);
397 for (Idx
=0; Idx
<ProcessCount
; Idx
++) {
398 // Get the old perf data for this process (if any)
399 // so that we can establish delta values
401 for (Idx2
=0; Idx2
<ProcessCountOld
; Idx2
++) {
402 if (pPerfDataOld
[Idx2
].ProcessId
== pSPI
->ProcessId
) {
403 pPDOld
= &pPerfDataOld
[Idx2
];
408 // Clear out process perf data structure
409 memset(&pPerfData
[Idx
], 0, sizeof(PERFDATA
));
411 if (pSPI
->ProcessName
.Buffer
)
412 wcsncpy(pPerfData
[Idx
].ImageName
, pSPI
->ProcessName
.Buffer
, pSPI
->ProcessName
.MaximumLength
);
414 wcscpy(pPerfData
[Idx
].ImageName
, L
"System Idle Process");
416 pPerfData
[Idx
].ProcessId
= pSPI
->ProcessId
;
420 double CurTime
= Li2Double(pSPI
->KernelTime
) + Li2Double(pSPI
->UserTime
);
421 double OldTime
= Li2Double(pPDOld
->KernelTime
) + Li2Double(pPDOld
->UserTime
);
422 double CpuTime
= (CurTime
- OldTime
) / dbSystemTime
;
423 CpuTime
= CpuTime
* 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5;*/
425 pPerfData
[Idx
].CPUUsage
= (ULONG
)CpuTime
;
427 pPerfData
[Idx
].CPUUsage
= 0;
431 pPerfData
[Idx
].CPUTime
.QuadPart
= pSPI
->UserTime
.QuadPart
+ pSPI
->KernelTime
.QuadPart
;
432 pPerfData
[Idx
].WorkingSetSizeBytes
= pSPI
->VmCounters
.WorkingSetSize
;
433 pPerfData
[Idx
].PeakWorkingSetSizeBytes
= pSPI
->VmCounters
.PeakWorkingSetSize
;
435 pPerfData
[Idx
].WorkingSetSizeDelta
= labs((LONG
)pSPI
->VmCounters
.WorkingSetSize
- (LONG
)pPDOld
->WorkingSetSizeBytes
);
437 pPerfData
[Idx
].WorkingSetSizeDelta
= 0;
438 pPerfData
[Idx
].PageFaultCount
= pSPI
->VmCounters
.PageFaultCount
;
440 pPerfData
[Idx
].PageFaultCountDelta
= labs((LONG
)pSPI
->VmCounters
.PageFaultCount
- (LONG
)pPDOld
->PageFaultCount
);
442 pPerfData
[Idx
].PageFaultCountDelta
= 0;
443 pPerfData
[Idx
].VirtualMemorySizeBytes
= pSPI
->VmCounters
.VirtualSize
;
444 pPerfData
[Idx
].PagedPoolUsagePages
= pSPI
->VmCounters
.QuotaPagedPoolUsage
;
445 pPerfData
[Idx
].NonPagedPoolUsagePages
= pSPI
->VmCounters
.QuotaNonPagedPoolUsage
;
446 pPerfData
[Idx
].BasePriority
= pSPI
->BasePriority
;
447 pPerfData
[Idx
].HandleCount
= pSPI
->HandleCount
;
448 pPerfData
[Idx
].ThreadCount
= pSPI
->ThreadCount
;
449 //pPerfData[Idx].SessionId = pSPI->SessionId;
452 hProcess
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, pSPI
->ProcessId
);
454 if (OpenProcessToken(hProcess
, TOKEN_QUERY
|TOKEN_DUPLICATE
|TOKEN_IMPERSONATE
, &hProcessToken
)) {
455 ImpersonateLoggedOnUser(hProcessToken
);
456 memset(szTemp
, 0, sizeof(TCHAR
[MAX_PATH
]));
458 GetUserName(szTemp
, &dwSize
);
460 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, szTemp
, -1, pPerfData
[Idx
].UserName
, MAX_PATH
);
462 int MultiByteToWideChar(
463 UINT CodePage, // code page
464 DWORD dwFlags, // character-type options
465 LPCSTR lpMultiByteStr, // string to map
466 int cbMultiByte, // number of bytes in string
467 LPWSTR lpWideCharStr, // wide-character buffer
468 int cchWideChar // size of buffer
473 CloseHandle(hProcessToken
);
475 CloseHandle(hProcess
);
479 pPerfData
[Idx
].UserTime
.QuadPart
= pSPI
->UserTime
.QuadPart
;
480 pPerfData
[Idx
].KernelTime
.QuadPart
= pSPI
->KernelTime
.QuadPart
;
482 pSPI
= PsaWalkNextProcess(pSPI
);
485 PsaFreeCapture(pBuffer
);
487 free(SysProcessorTimeInfo
);
490 // Code partly taken from slw32tty.c from mc/slang
491 unsigned int GetKeyPressed(int events
)
499 for (i
=0; i
<events
; i
++)
501 if (!ReadConsoleInput(hStdin
, &record
, 1, &bytesRead
)) {
505 if (record
.EventType
== KEY_EVENT
&& record
.Event
.KeyEvent
.bKeyDown
)
506 return record
.Event
.KeyEvent
.wVirtualKeyCode
;//.uChar.AsciiChar;
513 int main(int *argc
, char **argv
)
515 GetInputOutputHandles();
517 if (hStdin
== INVALID_HANDLE_VALUE
|| hStdout
== INVALID_HANDLE_VALUE
)
519 printf("ctm: can't use console.");
523 if (GetConsoleMode(hStdin
, &inConMode
) == 0)
525 printf("ctm: can't GetConsoleMode() for input console.");
529 if (GetConsoleMode(hStdout
, &outConMode
) == 0)
531 printf("ctm: can't GetConsoleMode() for output console.");
535 SetConsoleMode(hStdin
, 0); //FIXME: Should check for error!
536 SetConsoleMode(hStdout
, 0); //FIXME: Should check for error!
547 //WriteConsole(hStdin, " ", 1, &numEvents, NULL); // TODO: Make another way (this is ugly, I know)
548 GetNumberOfConsoleInputEvents(hStdin
, &numEvents
);
552 if (ProcessKeys(numEvents
) == TRUE
)
556 Sleep(40); // TODO: Should be done more efficient (might be another thread handling input/etc)*/