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)
10 Localization features added by Hervé Poussineau (hpoussineau@fr.st)
13 24 October 2004 - added localization features
14 09 April 2003 - v0.1, fixed bugs, added features, ported to mingw
15 20 March 2003 - v0.03, works good under ReactOS, and allows process
17 18 March 2003 - Initial version 0.01, doesn't work under RectOS
19 This program is free software; you can redistribute it and/or modify
20 it under the terms of the GNU General Public License as published by
21 the Free Software Foundation; either version 2 of the License, or
22 (at your option) any later version.
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
34 //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows //headers
43 #include <ddk/ntddk.h>
45 #include <ntos/zwtypes.h>
60 DWORD columnRightPositions
[5];
61 TCHAR lpSeparator
[80];
64 TCHAR lpIdleProcess
[80];;
70 TCHAR KEY_QUIT
, KEY_KILL
;
71 TCHAR KEY_YES
, KEY_NO
;
73 const int ProcPerScreen
= 17; // 17 processess are displayed on one page
74 ULONG ProcessCountOld
= 0;
75 ULONG ProcessCount
= 0;
80 LARGE_INTEGER liOldIdleTime
= {{0,0}};
81 double OldKernelTime
= 0;
82 LARGE_INTEGER liOldSystemTime
= {{0,0}};
84 PPERFDATA pPerfDataOld
= NULL
; // Older perf data (saved to establish delta values)
85 PPERFDATA pPerfData
= NULL
; // Most recent copy of perf data
88 int scrolled
=0; // offset from which process start showing
89 int first
= 0; // first time in DisplayScreen
90 SYSTEM_BASIC_INFORMATION SystemBasicInfo
;
94 // Functions that are needed by epsapi
95 void *PsaiMalloc(SIZE_T size
) { return malloc(size
); }
96 void *PsaiRealloc(void *ptr
, SIZE_T size
) { return realloc(ptr
, size
); }
97 void PsaiFree(void *ptr
) { free(ptr
); }
100 unsigned int GetKeyPressed();
102 void GetInputOutputHandles()
105 HANDLE console
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
106 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
107 0, CONSOLE_TEXTMODE_BUFFER
, 0);
109 if (SetConsoleActiveScreenBuffer(console
) == FALSE
)
111 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
112 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
116 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);//console;
120 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
121 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
125 void RestoreConsole()
127 SetConsoleMode(hStdin
, inConMode
);
128 SetConsoleMode(hStdout
, outConMode
);
131 SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE
));
147 pos
.X
= 2; pos
.Y
= 2;
148 WriteConsoleOutputCharacter(hStdout
, lpTitle
, _tcslen(lpTitle
), pos
, &numChars
);
150 pos
.X
= 2; pos
.Y
= 3;
151 WriteConsoleOutputCharacter(hStdout
, lpSeparator
, _tcslen(lpSeparator
), pos
, &numChars
);
153 pos
.X
= 2; pos
.Y
= 4;
154 WriteConsoleOutputCharacter(hStdout
, lpHeader
, _tcslen(lpHeader
), pos
, &numChars
);
156 pos
.X
= 2; pos
.Y
= 5;
157 WriteConsoleOutputCharacter(hStdout
, lpSeparator
, _tcslen(lpSeparator
), pos
, &numChars
);
160 pos
.X
= 2; pos
.Y
= ProcPerScreen
+6;
161 WriteConsoleOutputCharacter(hStdout
, lpSeparator
, _tcslen(lpSeparator
), pos
, &numChars
);
164 pos
.X
= 2; pos
.Y
= ProcPerScreen
+7;
165 WriteConsoleOutputCharacter(hStdout
, lpEmpty
, _tcslen(lpEmpty
), pos
, &numChars
);
166 WriteConsoleOutputCharacter(hStdout
, lpMenu
, _tcslen(lpMenu
), pos
, &numChars
);
172 lines
= ProcessCount
;
173 if (lines
> MAX_PROC
)
175 for (idx
=0; idx
<MAX_PROC
; idx
++)
178 TCHAR imgName
[MAX_PATH
];
182 TCHAR lpPageFaults
[15];
185 for (i
= 0; i
< 80; i
++)
189 if (idx
< lines
&& scrolled
+ idx
< ProcessCount
)
193 len
= wcslen(pPerfData
[scrolled
+idx
].ImageName
);
195 WideCharToMultiByte(CP_ACP
, 0, pPerfData
[scrolled
+idx
].ImageName
, -1,
196 imgName
, MAX_PATH
, NULL
, NULL
);
197 len
= strlen(imgName
);
199 if (len
> columnRightPositions
[0])
201 len
= columnRightPositions
[0];
204 wcsncpy(&lpStr
[2], pPerfData
[scrolled
+idx
].ImageName
, len
);
206 strncpy(&lpStr
[2], imgName
, len
);
210 _stprintf(lpPid
, _T("%6ld"), pPerfData
[scrolled
+idx
].ProcessId
);
211 _tcsncpy(&lpStr
[columnRightPositions
[1] - 6], lpPid
, 6);
215 _stprintf(lpCpu
, _T("%3d%%"), pPerfData
[scrolled
+idx
].CPUUsage
);
216 _tcsncpy(&lpStr
[columnRightPositions
[2] - 4], lpCpu
, 4);
220 _stprintf(lpMemUsg
, _T("%6ld %s"), pPerfData
[scrolled
+idx
].WorkingSetSizeBytes
/ 1024, lpMemUnit
);
221 _tcsncpy(&lpStr
[columnRightPositions
[3] - 9], lpMemUsg
, 9);
224 _stprintf(lpPageFaults
, _T("%12ld"), pPerfData
[scrolled
+idx
].PageFaultCount
);
225 _tcsncpy(&lpStr
[columnRightPositions
[4] - 12], lpPageFaults
, 12);
231 for (i
= 0; i
< 5; i
++)
232 lpStr
[columnRightPositions
[i
] + 1] = _T('|');
233 pos
.X
= 1; pos
.Y
= 6+idx
;
234 WriteConsoleOutputCharacter(hStdout
, lpStr
, 74, pos
, &numChars
);
237 pos
.X
= 3; pos
.Y
= 6+idx
;
238 if (selection
== idx
)
240 wColor
= BACKGROUND_GREEN
|
247 wColor
= BACKGROUND_BLUE
|
253 FillConsoleOutputAttribute(
254 hStdout
, // screen buffer handle
255 wColor
, // color to fill with
256 columnRightPositions
[0] - 1, // number of cells to fill
257 pos
, // first cell to write to
258 &numChars
); // actual number written
264 // returns TRUE if exiting
265 int ProcessKeys(int numEvents
)
268 if ((ProcessCount
-scrolled
< 17) && (ProcessCount
> 17))
269 scrolled
= ProcessCount
-17;
271 TCHAR key
= GetKeyPressed(numEvents
);
274 else if (key
== KEY_KILL
)
276 // user wants to kill some process, get his acknowledgement
281 pos
.X
= 2; pos
.Y
= 24;
282 if (LoadString(hInst
, IDS_KILL_PROCESS
, lpStr
, 100))
283 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
286 GetNumberOfConsoleInputEvents(hStdin
, &pId
);
287 key
= GetKeyPressed(pId
);
288 } while (key
!= KEY_YES
&& key
!= KEY_NO
);
293 pId
= pPerfData
[selection
+scrolled
].ProcessId
;
294 hProcess
= OpenProcess(PROCESS_TERMINATE
, FALSE
, pId
);
298 if (!TerminateProcess(hProcess
, 0))
300 if (LoadString(hInst
, IDS_KILL_PROCESS_ERR1
, lpStr
, 80))
302 WriteConsoleOutputCharacter(hStdout
, lpEmpty
, _tcslen(lpEmpty
), pos
, &numChars
);
303 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
308 CloseHandle(hProcess
);
312 if (LoadString(hInst
, IDS_KILL_PROCESS_ERR2
, lpStr
, 80))
314 WriteConsoleOutputCharacter(hStdout
, lpEmpty
, _tcslen(lpEmpty
), pos
, &numChars
);
315 _stprintf(lpStr
, lpStr
, pId
);
316 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
324 else if (key
== VK_UP
)
328 else if ((selection
== 0) && (scrolled
> 0))
331 else if (key
== VK_DOWN
)
333 if ((selection
< MAX_PROC
-1) && (selection
< ProcessCount
-1))
335 else if ((selection
== MAX_PROC
-1) && (selection
+scrolled
< ProcessCount
-1))
344 NtQuerySystemInformation(SystemBasicInformation
, &SystemBasicInfo
, sizeof(SystemBasicInfo
), 0);
347 void PerfDataRefresh()
355 HANDLE hProcessToken
;
356 PSYSTEM_PROCESSES pSPI
;
358 TCHAR szTemp
[MAX_PATH
];
360 double CurrentKernelTime
;
361 PSYSTEM_PROCESSORTIME_INFO SysProcessorTimeInfo
;
362 SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo
;
363 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo
;
366 // Get new system time
367 status
= NtQuerySystemInformation(SystemTimeInformation
, &SysTimeInfo
, sizeof(SysTimeInfo
), 0);
368 if (status
!= NO_ERROR
)
371 // Get new CPU's idle time
372 status
= NtQuerySystemInformation(SystemPerformanceInformation
, &SysPerfInfo
, sizeof(SysPerfInfo
), NULL
);
373 if (status
!= NO_ERROR
)
376 // Get processor information
377 SysProcessorTimeInfo
= (PSYSTEM_PROCESSORTIME_INFO
)malloc(sizeof(SYSTEM_PROCESSORTIME_INFO
) * SystemBasicInfo
.NumberProcessors
);
378 status
= NtQuerySystemInformation(SystemProcessorTimes
, SysProcessorTimeInfo
, sizeof(SYSTEM_PROCESSORTIME_INFO
) * SystemBasicInfo
.NumberProcessors
, &ulSize
);
381 // Get process information
382 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESSES
*)&pBuffer
);
385 for (CurrentKernelTime
=0, Idx
=0; Idx
<SystemBasicInfo
.NumberProcessors
; Idx
++) {
386 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].TotalProcessorTime
);
387 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].TotalDPCTime
);
388 CurrentKernelTime
+= Li2Double(SysProcessorTimeInfo
[Idx
].TotalInterruptTime
);
391 // If it's a first call - skip idle time calcs
392 if (liOldIdleTime
.QuadPart
!= 0) {
393 // CurrentValue = NewValue - OldValue
394 dbIdleTime
= Li2Double(SysPerfInfo
.IdleTime
) - Li2Double(liOldIdleTime
);
395 dbKernelTime
= CurrentKernelTime
- OldKernelTime
;
396 dbSystemTime
= Li2Double(SysTimeInfo
.CurrentTime
) - Li2Double(liOldSystemTime
);
398 // CurrentCpuIdle = IdleTime / SystemTime
399 dbIdleTime
= dbIdleTime
/ dbSystemTime
;
400 dbKernelTime
= dbKernelTime
/ dbSystemTime
;
402 // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
403 dbIdleTime
= 100.0 - dbIdleTime
* 100.0 / (double)SystemBasicInfo
.NumberProcessors
;// + 0.5;
404 dbKernelTime
= 100.0 - dbKernelTime
* 100.0 / (double)SystemBasicInfo
.NumberProcessors
;// + 0.5;
407 // Store new CPU's idle and system time
408 liOldIdleTime
= SysPerfInfo
.IdleTime
;
409 liOldSystemTime
= SysTimeInfo
.CurrentTime
;
410 OldKernelTime
= CurrentKernelTime
;
413 // Determine the process count
414 // We loop through the data we got from PsaCaptureProcessesAndThreads
415 // and count how many structures there are (until PsaWalkNextProcess
417 ProcessCountOld
= ProcessCount
;
419 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESSES
)pBuffer
);
422 pSPI
= PsaWalkNextProcess(pSPI
);
425 // Now alloc a new PERFDATA array and fill in the data
429 pPerfDataOld
= pPerfData
;
430 pPerfData
= (PPERFDATA
)malloc(sizeof(PERFDATA
) * ProcessCount
);
431 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESSES
)pBuffer
);
432 for (Idx
=0; Idx
<ProcessCount
; Idx
++) {
433 // Get the old perf data for this process (if any)
434 // so that we can establish delta values
436 for (Idx2
=0; Idx2
<ProcessCountOld
; Idx2
++) {
437 if (pPerfDataOld
[Idx2
].ProcessId
== pSPI
->ProcessId
) {
438 pPDOld
= &pPerfDataOld
[Idx2
];
443 // Clear out process perf data structure
444 memset(&pPerfData
[Idx
], 0, sizeof(PERFDATA
));
446 if (pSPI
->ProcessName
.Buffer
) {
447 wcsncpy(pPerfData
[Idx
].ImageName
, pSPI
->ProcessName
.Buffer
, pSPI
->ProcessName
.Length
/ sizeof(WCHAR
));
448 pPerfData
[Idx
].ImageName
[pSPI
->ProcessName
.Length
/ sizeof(WCHAR
)] = 0;
453 wcscpy(pPerfData
[Idx
].ImageName
, lpIdleProcess
);
455 MultiByteToWideChar(CP_ACP
, 0, lpIdleProcess
, strlen(lpIdleProcess
), pPerfData
[Idx
].ImageName
, MAX_PATH
);
459 pPerfData
[Idx
].ProcessId
= pSPI
->ProcessId
;
463 double CurTime
= Li2Double(pSPI
->KernelTime
) + Li2Double(pSPI
->UserTime
);
464 double OldTime
= Li2Double(pPDOld
->KernelTime
) + Li2Double(pPDOld
->UserTime
);
465 double CpuTime
= (CurTime
- OldTime
) / dbSystemTime
;
466 CpuTime
= CpuTime
* 100.0 / (double)SystemBasicInfo
.NumberProcessors
; // + 0.5;
468 pPerfData
[Idx
].CPUUsage
= (ULONG
)CpuTime
;
470 pPerfData
[Idx
].CPUUsage
= 0;
474 pPerfData
[Idx
].CPUTime
.QuadPart
= pSPI
->UserTime
.QuadPart
+ pSPI
->KernelTime
.QuadPart
;
475 pPerfData
[Idx
].WorkingSetSizeBytes
= pSPI
->VmCounters
.WorkingSetSize
;
476 pPerfData
[Idx
].PeakWorkingSetSizeBytes
= pSPI
->VmCounters
.PeakWorkingSetSize
;
478 pPerfData
[Idx
].WorkingSetSizeDelta
= labs((LONG
)pSPI
->VmCounters
.WorkingSetSize
- (LONG
)pPDOld
->WorkingSetSizeBytes
);
480 pPerfData
[Idx
].WorkingSetSizeDelta
= 0;
481 pPerfData
[Idx
].PageFaultCount
= pSPI
->VmCounters
.PageFaultCount
;
483 pPerfData
[Idx
].PageFaultCountDelta
= labs((LONG
)pSPI
->VmCounters
.PageFaultCount
- (LONG
)pPDOld
->PageFaultCount
);
485 pPerfData
[Idx
].PageFaultCountDelta
= 0;
486 pPerfData
[Idx
].VirtualMemorySizeBytes
= pSPI
->VmCounters
.VirtualSize
;
487 pPerfData
[Idx
].PagedPoolUsagePages
= pSPI
->VmCounters
.QuotaPagedPoolUsage
;
488 pPerfData
[Idx
].NonPagedPoolUsagePages
= pSPI
->VmCounters
.QuotaNonPagedPoolUsage
;
489 pPerfData
[Idx
].BasePriority
= pSPI
->BasePriority
;
490 pPerfData
[Idx
].HandleCount
= pSPI
->HandleCount
;
491 pPerfData
[Idx
].ThreadCount
= pSPI
->ThreadCount
;
492 //pPerfData[Idx].SessionId = pSPI->SessionId;
495 hProcess
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, pSPI
->ProcessId
);
497 if (OpenProcessToken(hProcess
, TOKEN_QUERY
|TOKEN_DUPLICATE
|TOKEN_IMPERSONATE
, &hProcessToken
)) {
498 ImpersonateLoggedOnUser(hProcessToken
);
499 memset(szTemp
, 0, sizeof(TCHAR
[MAX_PATH
]));
501 GetUserName(szTemp
, &dwSize
);
503 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, szTemp
, -1, pPerfData
[Idx
].UserName
, MAX_PATH
);
505 int MultiByteToWideChar(
506 UINT CodePage, // code page
507 DWORD dwFlags, // character-type options
508 LPCSTR lpMultiByteStr, // string to map
509 int cbMultiByte, // number of bytes in string
510 LPWSTR lpWideCharStr, // wide-character buffer
511 int cchWideChar // size of buffer
516 CloseHandle(hProcessToken
);
518 CloseHandle(hProcess
);
522 pPerfData
[Idx
].UserTime
.QuadPart
= pSPI
->UserTime
.QuadPart
;
523 pPerfData
[Idx
].KernelTime
.QuadPart
= pSPI
->KernelTime
.QuadPart
;
525 pSPI
= PsaWalkNextProcess(pSPI
);
527 PsaFreeCapture(pBuffer
);
529 free(SysProcessorTimeInfo
);
532 // Code partly taken from slw32tty.c from mc/slang
533 unsigned int GetKeyPressed(int events
)
541 for (i
=0; i
<events
; i
++)
543 if (!ReadConsoleInput(hStdin
, &record
, 1, &bytesRead
)) {
547 if (record
.EventType
== KEY_EVENT
&& record
.Event
.KeyEvent
.bKeyDown
)
548 return record
.Event
.KeyEvent
.wVirtualKeyCode
;//.uChar.AsciiChar;
555 int main(int *argc
, char **argv
)
560 for (i
= 0; i
< 80; i
++)
561 lpEmpty
[i
] = lpHeader
[i
] = _T(' ');
562 lpEmpty
[79] = _T('\0');
564 /* Initialize global variables */
565 hInst
= 0 /* FIXME: which value? [used with LoadString(hInst, ..., ..., ...)] */;
566 if (LoadString(hInst
, IDS_COLUMN_IMAGENAME
, lpStr
, 80))
568 columnRightPositions
[0] = _tcslen(lpStr
);
569 _tcsncpy(&lpHeader
[2], lpStr
, _tcslen(lpStr
));
571 if (LoadString(hInst
, IDS_COLUMN_PID
, lpStr
, 80))
573 columnRightPositions
[1] = columnRightPositions
[0] + _tcslen(lpStr
) + 3;
574 _tcsncpy(&lpHeader
[columnRightPositions
[0] + 2], lpStr
, _tcslen(lpStr
));
576 if (LoadString(hInst
, IDS_COLUMN_CPU
, lpStr
, 80))
578 columnRightPositions
[2] = columnRightPositions
[1] + _tcslen(lpStr
) + 3;
579 _tcsncpy(&lpHeader
[columnRightPositions
[1] + 2], lpStr
, _tcslen(lpStr
));
581 if (LoadString(hInst
, IDS_COLUMN_MEM
, lpStr
, 80))
583 columnRightPositions
[3] = columnRightPositions
[2] + _tcslen(lpStr
) + 3;
584 _tcsncpy(&lpHeader
[columnRightPositions
[2] + 2], lpStr
, _tcslen(lpStr
));
586 if (LoadString(hInst
, IDS_COLUMN_PF
, lpStr
, 80))
588 columnRightPositions
[4] = columnRightPositions
[3] + _tcslen(lpStr
) + 3;
589 _tcsncpy(&lpHeader
[columnRightPositions
[3] + 2], lpStr
, _tcslen(lpStr
));
592 for (i
= 0; i
< columnRightPositions
[4]; i
++)
593 lpSeparator
[i
] = _T('-');
594 lpHeader
[0] = _T('|');
595 lpSeparator
[0] = _T('+');
596 for (i
= 0; i
< 5; i
++)
598 lpHeader
[columnRightPositions
[i
]] = _T('|');
599 lpSeparator
[columnRightPositions
[i
]] = _T('+');
601 lpSeparator
[columnRightPositions
[4] + 1] = _T('\0');
602 lpHeader
[columnRightPositions
[4] + 1] = _T('\0');
605 if (!LoadString(hInst
, IDS_APP_TITLE
, lpTitle
, 80))
606 lpTitle
[0] = _T('\0');
607 if (!LoadString(hInst
, IDS_COLUMN_MEM_UNIT
, lpMemUnit
, 3))
608 lpMemUnit
[0] = _T('\0');
609 if (!LoadString(hInst
, IDS_MENU
, lpMenu
, 80))
610 lpMenu
[0] = _T('\0');
611 if (!LoadString(hInst
, IDS_IDLE_PROCESS
, lpIdleProcess
, 80))
612 lpIdleProcess
[0] = _T('\0');
614 if (LoadString(hInst
, IDS_MENU_QUIT
, lpStr
, 2))
616 if (LoadString(hInst
, IDS_MENU_KILL_PROCESS
, lpStr
, 2))
618 if (LoadString(hInst
, IDS_YES
, lpStr
, 2))
620 if (LoadString(hInst
, IDS_NO
, lpStr
, 2))
623 GetInputOutputHandles();
625 if (hStdin
== INVALID_HANDLE_VALUE
|| hStdout
== INVALID_HANDLE_VALUE
)
627 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR1
, lpStr
, 80))
632 if (GetConsoleMode(hStdin
, &inConMode
) == 0)
634 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR2
, lpStr
, 80))
639 if (GetConsoleMode(hStdout
, &outConMode
) == 0)
641 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR3
, lpStr
, 80))
646 SetConsoleMode(hStdin
, 0); //FIXME: Should check for error!
647 SetConsoleMode(hStdout
, 0); //FIXME: Should check for error!
658 /* WaitForSingleObject for console handles is not implemented in ROS */
659 WaitForSingleObject(hStdin
, 1000);
660 GetNumberOfConsoleInputEvents(hStdin
, &numEvents
);
664 if (ProcessKeys(numEvents
) == TRUE
)