Merge 15329:15546 from trunk
[reactos.git] / rosapps / sysutils / ctm / ctm.c
1 /* Console Task Manager
2
3 ctm.c - main program file
4
5 Written by: Aleksey Bragin (aleksey@studiocerebral.com)
6
7 Most of the code dealing with getting system parameters is taken from
8 ReactOS Task Manager written by Brian Palmer (brianp@reactos.org)
9
10 Localization features added by Hervé Poussineau (hpoussineau@fr.st)
11
12 History:
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
16 killing
17 18 March 2003 - Initial version 0.01, doesn't work under RectOS
18
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.
23
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.
28
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. */
32
33
34 //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows //headers
35 #include <windows.h>
36 #include <stdlib.h>
37 #include <malloc.h>
38 #include <memory.h>
39 #include <tchar.h>
40 #include <process.h>
41 #include <stdio.h>
42
43 #include <ddk/ntddk.h>
44 #include <epsapi.h>
45 #include <ntos/zwtypes.h>
46
47 #include "ctm.h"
48 #include "resource.h"
49
50 #define MAX_PROC 17
51 #define TIMES
52
53 HANDLE hStdin;
54 HANDLE hStdout;
55 HINSTANCE hInst;
56
57 DWORD inConMode;
58 DWORD outConMode;
59
60 DWORD columnRightPositions[5];
61 TCHAR lpSeparator[80];
62 TCHAR lpHeader[80];
63 TCHAR lpMemUnit[3];
64 TCHAR lpIdleProcess[80];;
65 TCHAR lpTitle[80];
66 TCHAR lpHeader[80];
67 TCHAR lpMenu[80];
68 TCHAR lpEmpty[80];
69
70 TCHAR KEY_QUIT, KEY_KILL;
71 TCHAR KEY_YES, KEY_NO;
72
73 const int ProcPerScreen = 17; // 17 processess are displayed on one page
74 ULONG ProcessCountOld = 0;
75 ULONG ProcessCount = 0;
76
77 double dbIdleTime;
78 double dbKernelTime;
79 double dbSystemTime;
80 LARGE_INTEGER liOldIdleTime = {{0,0}};
81 LARGE_INTEGER liOldKernelTime = {{0,0}};
82 LARGE_INTEGER liOldSystemTime = {{0,0}};
83
84 PPERFDATA pPerfDataOld = NULL; // Older perf data (saved to establish delta values)
85 PPERFDATA pPerfData = NULL; // Most recent copy of perf data
86
87 int selection=0;
88 int scrolled=0; // offset from which process start showing
89 int first = 0; // first time in DisplayScreen
90 SYSTEM_BASIC_INFORMATION SystemBasicInfo;
91
92 #define NEW_CONSOLE
93
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); }
98
99 // Prototypes
100 unsigned int GetKeyPressed();
101
102 void GetInputOutputHandles()
103 {
104 #ifdef NEW_CONSOLE
105 HANDLE console = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
106 FILE_SHARE_READ | FILE_SHARE_WRITE,
107 0, CONSOLE_TEXTMODE_BUFFER, 0);
108
109 if (SetConsoleActiveScreenBuffer(console) == FALSE)
110 {
111 hStdin = GetStdHandle(STD_INPUT_HANDLE);
112 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
113 }
114 else
115 {
116 hStdin = GetStdHandle(STD_INPUT_HANDLE);//console;
117 hStdout = console;
118 }
119 #else
120 hStdin = GetStdHandle(STD_INPUT_HANDLE);
121 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
122 #endif
123 }
124
125 void RestoreConsole()
126 {
127 SetConsoleMode(hStdin, inConMode);
128 SetConsoleMode(hStdout, outConMode);
129
130 #ifdef NEW_CONSOLE
131 SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
132 #endif
133 }
134
135 void DisplayScreen()
136 {
137 COORD pos;
138 TCHAR lpStr[80];
139 int posStr;
140 DWORD numChars;
141 int lines;
142 int idx, i;
143
144 if (first == 0)
145 {
146 // Header
147 pos.X = 2; pos.Y = 2;
148 WriteConsoleOutputCharacter(hStdout, lpTitle, _tcslen(lpTitle), pos, &numChars);
149
150 pos.X = 2; pos.Y = 3;
151 WriteConsoleOutputCharacter(hStdout, lpSeparator, _tcslen(lpSeparator), pos, &numChars);
152
153 pos.X = 2; pos.Y = 4;
154 WriteConsoleOutputCharacter(hStdout, lpHeader, _tcslen(lpHeader), pos, &numChars);
155
156 pos.X = 2; pos.Y = 5;
157 WriteConsoleOutputCharacter(hStdout, lpSeparator, _tcslen(lpSeparator), pos, &numChars);
158
159 // Footer
160 pos.X = 2; pos.Y = ProcPerScreen+6;
161 WriteConsoleOutputCharacter(hStdout, lpSeparator, _tcslen(lpSeparator), pos, &numChars);
162
163 // Menu
164 pos.X = 2; pos.Y = ProcPerScreen+7;
165 WriteConsoleOutputCharacter(hStdout, lpEmpty, _tcslen(lpEmpty), pos, &numChars);
166 WriteConsoleOutputCharacter(hStdout, lpMenu, _tcslen(lpMenu), pos, &numChars);
167
168 first = 1;
169 }
170
171 // Processess
172 lines = ProcessCount;
173 if (lines > MAX_PROC)
174 lines = MAX_PROC;
175 for (idx=0; idx<MAX_PROC; idx++)
176 {
177 int len, i;
178 TCHAR imgName[MAX_PATH];
179 TCHAR lpPid[8];
180 TCHAR lpCpu[6];
181 TCHAR lpMemUsg[12];
182 TCHAR lpPageFaults[15];
183 WORD wColor;
184
185 for (i = 0; i < 80; i++)
186 lpStr[i] = _T(' ');
187
188 // data
189 if (idx < lines && scrolled + idx < ProcessCount)
190 {
191 // image name
192 #ifdef _UNICODE
193 len = wcslen(pPerfData[scrolled+idx].ImageName);
194 #else
195 WideCharToMultiByte(CP_ACP, 0, pPerfData[scrolled+idx].ImageName, -1,
196 imgName, MAX_PATH, NULL, NULL);
197 len = strlen(imgName);
198 #endif
199 if (len > columnRightPositions[0])
200 {
201 len = columnRightPositions[0];
202 }
203 #ifdef _UNICODE
204 wcsncpy(&lpStr[2], pPerfData[scrolled+idx].ImageName, len);
205 #else
206 strncpy(&lpStr[2], imgName, len);
207 #endif
208
209 // PID
210 _stprintf(lpPid, _T("%6ld"), pPerfData[scrolled+idx].ProcessId);
211 _tcsncpy(&lpStr[columnRightPositions[1] - 6], lpPid, 6);
212
213 #ifdef TIMES
214 // CPU
215 _stprintf(lpCpu, _T("%3d%%"), pPerfData[scrolled+idx].CPUUsage);
216 _tcsncpy(&lpStr[columnRightPositions[2] - 4], lpCpu, 4);
217 #endif
218
219 // Mem usage
220 _stprintf(lpMemUsg, _T("%6ld %s"), pPerfData[scrolled+idx].WorkingSetSizeBytes / 1024, lpMemUnit);
221 _tcsncpy(&lpStr[columnRightPositions[3] - 9], lpMemUsg, 9);
222
223 // Page Fault
224 _stprintf(lpPageFaults, _T("%12ld"), pPerfData[scrolled+idx].PageFaultCount);
225 _tcsncpy(&lpStr[columnRightPositions[4] - 12], lpPageFaults, 12);
226 }
227
228 // columns
229 lpStr[0] = _T(' ');
230 lpStr[1] = _T('|');
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);
235
236 // Attributes now...
237 pos.X = 3; pos.Y = 6+idx;
238 if (selection == idx)
239 {
240 wColor = BACKGROUND_GREEN |
241 FOREGROUND_RED |
242 FOREGROUND_GREEN |
243 FOREGROUND_BLUE;
244 }
245 else
246 {
247 wColor = BACKGROUND_BLUE |
248 FOREGROUND_RED |
249 FOREGROUND_GREEN |
250 FOREGROUND_BLUE;
251 }
252
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
259 }
260
261 return;
262 }
263
264 // returns TRUE if exiting
265 int ProcessKeys(int numEvents)
266 {
267 DWORD numChars;
268 if ((ProcessCount-scrolled < 17) && (ProcessCount > 17))
269 scrolled = ProcessCount-17;
270
271 TCHAR key = GetKeyPressed(numEvents);
272 if (key == KEY_QUIT)
273 return TRUE;
274 else if (key == KEY_KILL)
275 {
276 // user wants to kill some process, get his acknowledgement
277 DWORD pId;
278 COORD pos;
279 TCHAR lpStr[100];
280
281 pos.X = 2; pos.Y = 24;
282 if (LoadString(hInst, IDS_KILL_PROCESS, lpStr, 100))
283 WriteConsoleOutputCharacter(hStdout, lpStr, _tcslen(lpStr), pos, &numChars);
284
285 do {
286 GetNumberOfConsoleInputEvents(hStdin, &pId);
287 key = GetKeyPressed(pId);
288 } while (key != KEY_YES && key != KEY_NO);
289
290 if (key == KEY_YES)
291 {
292 HANDLE hProcess;
293 pId = pPerfData[selection+scrolled].ProcessId;
294 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pId);
295
296 if (hProcess)
297 {
298 if (!TerminateProcess(hProcess, 0))
299 {
300 if (LoadString(hInst, IDS_KILL_PROCESS_ERR1, lpStr, 80))
301 {
302 WriteConsoleOutputCharacter(hStdout, lpEmpty, _tcslen(lpEmpty), pos, &numChars);
303 WriteConsoleOutputCharacter(hStdout, lpStr, _tcslen(lpStr), pos, &numChars);
304 }
305 Sleep(1000);
306 }
307
308 CloseHandle(hProcess);
309 }
310 else
311 {
312 if (LoadString(hInst, IDS_KILL_PROCESS_ERR2, lpStr, 80))
313 {
314 WriteConsoleOutputCharacter(hStdout, lpEmpty, _tcslen(lpEmpty), pos, &numChars);
315 _stprintf(lpStr, lpStr, pId);
316 WriteConsoleOutputCharacter(hStdout, lpStr, _tcslen(lpStr), pos, &numChars);
317 }
318 Sleep(1000);
319 }
320 }
321
322 first = 0;
323 }
324 else if (key == VK_UP)
325 {
326 if (selection > 0)
327 selection--;
328 else if ((selection == 0) && (scrolled > 0))
329 scrolled--;
330 }
331 else if (key == VK_DOWN)
332 {
333 if ((selection < MAX_PROC-1) && (selection < ProcessCount-1))
334 selection++;
335 else if ((selection == MAX_PROC-1) && (selection+scrolled < ProcessCount-1))
336 scrolled++;
337 }
338
339 return FALSE;
340 }
341
342 void PerfInit()
343 {
344 NtQuerySystemInformation(SystemBasicInformation, &SystemBasicInfo, sizeof(SystemBasicInfo), 0);
345 }
346
347 void PerfDataRefresh()
348 {
349 LONG status;
350 ULONG ulSize;
351 LPBYTE pBuffer;
352 ULONG BufferSize;
353 ULONG Idx, Idx2;
354 HANDLE hProcess;
355 HANDLE hProcessToken;
356 PSYSTEM_PROCESS_INFORMATION pSPI;
357 PPERFDATA pPDOld;
358 TCHAR szTemp[MAX_PATH];
359 DWORD dwSize;
360 #ifdef TIMES
361 LARGE_INTEGER liCurrentKernelTime;
362 LARGE_INTEGER liCurrentIdleTime;
363 LARGE_INTEGER liCurrentTime;
364 #endif
365 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SysProcessorTimeInfo;
366 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo;
367
368 #ifdef TIMES
369 // Get new system time
370 status = NtQuerySystemInformation(SystemTimeInformation, &SysTimeInfo, sizeof(SysTimeInfo), 0);
371 if (status != NO_ERROR)
372 return;
373 #endif
374 // Get processor information
375 SysProcessorTimeInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)malloc(sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * SystemBasicInfo.NumberProcessors);
376 status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, SysProcessorTimeInfo, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * SystemBasicInfo.NumberProcessors, &ulSize);
377
378
379 // Get process information
380 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESS_INFORMATION *)&pBuffer);
381
382 #ifdef TIMES
383 liCurrentKernelTime.QuadPart = 0;
384 liCurrentIdleTime.QuadPart = 0;
385 for (Idx=0; Idx<SystemBasicInfo.NumberProcessors; Idx++) {
386 liCurrentKernelTime.QuadPart += SysProcessorTimeInfo[Idx].KernelTime.QuadPart;
387 liCurrentKernelTime.QuadPart += SysProcessorTimeInfo[Idx].DpcTime.QuadPart;
388 liCurrentKernelTime.QuadPart += SysProcessorTimeInfo[Idx].InterruptTime.QuadPart;
389 liCurrentIdleTime.QuadPart += SysProcessorTimeInfo[Idx].IdleTime.QuadPart;
390 }
391
392 // If it's a first call - skip idle time calcs
393 if (liOldIdleTime.QuadPart != 0) {
394 // CurrentValue = NewValue - OldValue
395 liCurrentTime.QuadPart = liCurrentIdleTime.QuadPart - liOldIdleTime.QuadPart;
396 dbIdleTime = Li2Double(liCurrentTime);
397 liCurrentTime.QuadPart = liCurrentKernelTime.QuadPart - liOldKernelTime.QuadPart;
398 dbKernelTime = Li2Double(liCurrentTime);
399 liCurrentTime.QuadPart = SysTimeInfo.CurrentTime.QuadPart - liOldSystemTime.QuadPart;
400 dbSystemTime = Li2Double(liCurrentTime);
401
402 // CurrentCpuIdle = IdleTime / SystemTime
403 dbIdleTime = dbIdleTime / dbSystemTime;
404 dbKernelTime = dbKernelTime / dbSystemTime;
405
406 // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
407 dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SystemBasicInfo.NumberProcessors;// + 0.5;
408 dbKernelTime = 100.0 - dbKernelTime * 100.0 / (double)SystemBasicInfo.NumberProcessors;// + 0.5;
409 }
410
411 // Store new CPU's idle and system time
412 liOldIdleTime = liCurrentIdleTime;
413 liOldSystemTime = SysTimeInfo.CurrentTime;
414 liOldKernelTime = liCurrentKernelTime;
415 #endif
416
417 // Determine the process count
418 // We loop through the data we got from PsaCaptureProcessesAndThreads
419 // and count how many structures there are (until PsaWalkNextProcess
420 // returns NULL)
421 ProcessCountOld = ProcessCount;
422 ProcessCount = 0;
423 pSPI = PsaWalkFirstProcess((PSYSTEM_PROCESS_INFORMATION)pBuffer);
424 while (pSPI) {
425 ProcessCount++;
426 pSPI = PsaWalkNextProcess(pSPI);
427 }
428
429 // Now alloc a new PERFDATA array and fill in the data
430 if (pPerfDataOld) {
431 free(pPerfDataOld);
432 }
433 pPerfDataOld = pPerfData;
434 pPerfData = (PPERFDATA)malloc(sizeof(PERFDATA) * ProcessCount);
435 pSPI = PsaWalkFirstProcess((PSYSTEM_PROCESS_INFORMATION)pBuffer);
436 for (Idx=0; Idx<ProcessCount; Idx++) {
437 // Get the old perf data for this process (if any)
438 // so that we can establish delta values
439 pPDOld = NULL;
440 for (Idx2=0; Idx2<ProcessCountOld; Idx2++) {
441 if (pPerfDataOld[Idx2].ProcessId == (ULONG)(pSPI->UniqueProcessId) &&
442 /* check also for the creation time, a new process may have an id of an old one */
443 pPerfDataOld[Idx2].CreateTime.QuadPart == pSPI->CreateTime.QuadPart) {
444 pPDOld = &pPerfDataOld[Idx2];
445 break;
446 }
447 }
448
449 // Clear out process perf data structure
450 memset(&pPerfData[Idx], 0, sizeof(PERFDATA));
451
452 if (pSPI->ImageName.Buffer) {
453 wcsncpy(pPerfData[Idx].ImageName, pSPI->ImageName.Buffer, pSPI->ImageName.Length / sizeof(WCHAR));
454 pPerfData[Idx].ImageName[pSPI->ImageName.Length / sizeof(WCHAR)] = 0;
455 }
456 else
457 {
458 #ifdef _UNICODE
459 wcscpy(pPerfData[Idx].ImageName, lpIdleProcess);
460 #else
461 MultiByteToWideChar(CP_ACP, 0, lpIdleProcess, strlen(lpIdleProcess), pPerfData[Idx].ImageName, MAX_PATH);
462 #endif
463 }
464
465 pPerfData[Idx].ProcessId = (ULONG)(pSPI->UniqueProcessId);
466 pPerfData[Idx].CreateTime = pSPI->CreateTime;
467
468 if (pPDOld) {
469 #ifdef TIMES
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.NumberProcessors; // + 0.5;
474
475 pPerfData[Idx].CPUUsage = (ULONG)CpuTime;
476 #else
477 pPerfData[Idx].CPUUsage = 0;
478 #endif
479 }
480
481 pPerfData[Idx].CPUTime.QuadPart = pSPI->UserTime.QuadPart + pSPI->KernelTime.QuadPart;
482 pPerfData[Idx].WorkingSetSizeBytes = pSPI->WorkingSetSize;
483 pPerfData[Idx].PeakWorkingSetSizeBytes = pSPI->PeakWorkingSetSize;
484 if (pPDOld)
485 pPerfData[Idx].WorkingSetSizeDelta = labs((LONG)pSPI->WorkingSetSize - (LONG)pPDOld->WorkingSetSizeBytes);
486 else
487 pPerfData[Idx].WorkingSetSizeDelta = 0;
488 pPerfData[Idx].PageFaultCount = pSPI->PageFaultCount;
489 if (pPDOld)
490 pPerfData[Idx].PageFaultCountDelta = labs((LONG)pSPI->PageFaultCount - (LONG)pPDOld->PageFaultCount);
491 else
492 pPerfData[Idx].PageFaultCountDelta = 0;
493 pPerfData[Idx].VirtualMemorySizeBytes = pSPI->VirtualSize;
494 pPerfData[Idx].PagedPoolUsagePages = pSPI->QuotaPagedPoolUsage;
495 pPerfData[Idx].NonPagedPoolUsagePages = pSPI->QuotaNonPagedPoolUsage;
496 pPerfData[Idx].BasePriority = pSPI->BasePriority;
497 pPerfData[Idx].HandleCount = pSPI->HandleCount;
498 pPerfData[Idx].ThreadCount = pSPI->NumberOfThreads;
499 //pPerfData[Idx].SessionId = pSPI->SessionId;
500
501 #ifdef EXTRA_INFO
502 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pSPI->UniqueProcessId);
503 if (hProcess) {
504 if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_IMPERSONATE, &hProcessToken)) {
505 ImpersonateLoggedOnUser(hProcessToken);
506 memset(szTemp, 0, sizeof(TCHAR[MAX_PATH]));
507 dwSize = MAX_PATH;
508 GetUserName(szTemp, &dwSize);
509 #ifndef UNICODE
510 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTemp, -1, pPerfData[Idx].UserName, MAX_PATH);
511 /*
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
519 );
520 */
521 #endif
522 RevertToSelf();
523 CloseHandle(hProcessToken);
524 }
525 CloseHandle(hProcess);
526 }
527 #endif
528 #ifdef TIMES
529 pPerfData[Idx].UserTime.QuadPart = pSPI->UserTime.QuadPart;
530 pPerfData[Idx].KernelTime.QuadPart = pSPI->KernelTime.QuadPart;
531 #endif
532 pSPI = PsaWalkNextProcess(pSPI);
533 }
534 PsaFreeCapture(pBuffer);
535
536 free(SysProcessorTimeInfo);
537 }
538
539 // Code partly taken from slw32tty.c from mc/slang
540 unsigned int GetKeyPressed(int events)
541 {
542 long key;
543 DWORD bytesRead;
544 INPUT_RECORD record;
545 int i;
546
547
548 for (i=0; i<events; i++)
549 {
550 if (!ReadConsoleInput(hStdin, &record, 1, &bytesRead)) {
551 return 0;
552 }
553
554 if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
555 return record.Event.KeyEvent.wVirtualKeyCode;//.uChar.AsciiChar;
556 }
557
558 return 0;
559 }
560
561
562 int main(int *argc, char **argv)
563 {
564 int i;
565 TCHAR lpStr[80];
566
567 for (i = 0; i < 80; i++)
568 lpEmpty[i] = lpHeader[i] = _T(' ');
569 lpEmpty[79] = _T('\0');
570
571 /* Initialize global variables */
572 hInst = 0 /* FIXME: which value? [used with LoadString(hInst, ..., ..., ...)] */;
573 if (LoadString(hInst, IDS_COLUMN_IMAGENAME, lpStr, 80))
574 {
575 columnRightPositions[0] = _tcslen(lpStr);
576 _tcsncpy(&lpHeader[2], lpStr, _tcslen(lpStr));
577 }
578 if (LoadString(hInst, IDS_COLUMN_PID, lpStr, 80))
579 {
580 columnRightPositions[1] = columnRightPositions[0] + _tcslen(lpStr) + 3;
581 _tcsncpy(&lpHeader[columnRightPositions[0] + 2], lpStr, _tcslen(lpStr));
582 }
583 if (LoadString(hInst, IDS_COLUMN_CPU, lpStr, 80))
584 {
585 columnRightPositions[2] = columnRightPositions[1] + _tcslen(lpStr) + 3;
586 _tcsncpy(&lpHeader[columnRightPositions[1] + 2], lpStr, _tcslen(lpStr));
587 }
588 if (LoadString(hInst, IDS_COLUMN_MEM, lpStr, 80))
589 {
590 columnRightPositions[3] = columnRightPositions[2] + _tcslen(lpStr) + 3;
591 _tcsncpy(&lpHeader[columnRightPositions[2] + 2], lpStr, _tcslen(lpStr));
592 }
593 if (LoadString(hInst, IDS_COLUMN_PF, lpStr, 80))
594 {
595 columnRightPositions[4] = columnRightPositions[3] + _tcslen(lpStr) + 3;
596 _tcsncpy(&lpHeader[columnRightPositions[3] + 2], lpStr, _tcslen(lpStr));
597 }
598
599 for (i = 0; i < columnRightPositions[4]; i++)
600 lpSeparator[i] = _T('-');
601 lpHeader[0] = _T('|');
602 lpSeparator[0] = _T('+');
603 for (i = 0; i < 5; i++)
604 {
605 lpHeader[columnRightPositions[i]] = _T('|');
606 lpSeparator[columnRightPositions[i]] = _T('+');
607 }
608 lpSeparator[columnRightPositions[4] + 1] = _T('\0');
609 lpHeader[columnRightPositions[4] + 1] = _T('\0');
610
611
612 if (!LoadString(hInst, IDS_APP_TITLE, lpTitle, 80))
613 lpTitle[0] = _T('\0');
614 if (!LoadString(hInst, IDS_COLUMN_MEM_UNIT, lpMemUnit, 3))
615 lpMemUnit[0] = _T('\0');
616 if (!LoadString(hInst, IDS_MENU, lpMenu, 80))
617 lpMenu[0] = _T('\0');
618 if (!LoadString(hInst, IDS_IDLE_PROCESS, lpIdleProcess, 80))
619 lpIdleProcess[0] = _T('\0');
620
621 if (LoadString(hInst, IDS_MENU_QUIT, lpStr, 2))
622 KEY_QUIT = lpStr[0];
623 if (LoadString(hInst, IDS_MENU_KILL_PROCESS, lpStr, 2))
624 KEY_KILL = lpStr[0];
625 if (LoadString(hInst, IDS_YES, lpStr, 2))
626 KEY_YES = lpStr[0];
627 if (LoadString(hInst, IDS_NO, lpStr, 2))
628 KEY_NO = lpStr[0];
629
630 GetInputOutputHandles();
631
632 if (hStdin == INVALID_HANDLE_VALUE || hStdout == INVALID_HANDLE_VALUE)
633 {
634 if (LoadString(hInst, IDS_CTM_GENERAL_ERR1, lpStr, 80))
635 _tprintf(lpStr);
636 return -1;
637 }
638
639 if (GetConsoleMode(hStdin, &inConMode) == 0)
640 {
641 if (LoadString(hInst, IDS_CTM_GENERAL_ERR2, lpStr, 80))
642 _tprintf(lpStr);
643 return -1;
644 }
645
646 if (GetConsoleMode(hStdout, &outConMode) == 0)
647 {
648 if (LoadString(hInst, IDS_CTM_GENERAL_ERR3, lpStr, 80))
649 _tprintf(lpStr);
650 return -1;
651 }
652
653 SetConsoleMode(hStdin, 0); //FIXME: Should check for error!
654 SetConsoleMode(hStdout, 0); //FIXME: Should check for error!
655
656 PerfInit();
657
658 while (1)
659 {
660 DWORD numEvents;
661
662 PerfDataRefresh();
663 DisplayScreen();
664
665 /* WaitForSingleObject for console handles is not implemented in ROS */
666 WaitForSingleObject(hStdin, 1000);
667 GetNumberOfConsoleInputEvents(hStdin, &numEvents);
668
669 if (numEvents > 0)
670 {
671 if (ProcessKeys(numEvents) == TRUE)
672 break;
673 }
674 }
675
676 RestoreConsole();
677 return 0;
678 }