64dfc7bba71e9c00df393eeb9bb9ee134601f22e
[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 (hpoussin@reactos.org)
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
37 #include <stdlib.h>
38 #include <malloc.h>
39 #include <memory.h>
40 #include <tchar.h>
41 #include <process.h>
42 #include <stdio.h>
43
44 #define NTOS_MODE_USER
45 #include <ndk/ntndk.h>
46
47 #include <epsapi/epsapi.h>
48
49 #include "ctm.h"
50 #include "resource.h"
51
52 #define MAX_PROC 17
53 #define TIMES
54
55 HANDLE hStdin;
56 HANDLE hStdout;
57 HINSTANCE hInst;
58
59 DWORD inConMode;
60 DWORD outConMode;
61
62 DWORD columnRightPositions[5];
63 TCHAR lpSeparator[80];
64 TCHAR lpHeader[80];
65 TCHAR lpMemUnit[3];
66 TCHAR lpIdleProcess[80];
67 TCHAR lpTitle[80];
68 TCHAR lpHeader[80];
69 TCHAR lpMenu[80];
70 TCHAR lpEmpty[80];
71
72 TCHAR KEY_QUIT, KEY_KILL;
73 TCHAR KEY_YES, KEY_NO;
74
75 const int ProcPerScreen = 17; // 17 processess are displayed on one page
76 ULONG ProcessCountOld = 0;
77 ULONG ProcessCount = 0;
78
79 double dbIdleTime;
80 double dbKernelTime;
81 double dbSystemTime;
82 LARGE_INTEGER liOldIdleTime = {{0,0}};
83 LARGE_INTEGER liOldKernelTime = {{0,0}};
84 LARGE_INTEGER liOldSystemTime = {{0,0}};
85
86 PPERFDATA pPerfDataOld = NULL; // Older perf data (saved to establish delta values)
87 PPERFDATA pPerfData = NULL; // Most recent copy of perf data
88
89 int selection=0;
90 int scrolled=0; // offset from which process start showing
91 int first = 0; // first time in DisplayScreen
92 SYSTEM_BASIC_INFORMATION SystemBasicInfo;
93
94 #define NEW_CONSOLE
95
96 // Functions that are needed by epsapi
97 void *PsaiMalloc(SIZE_T size) { return malloc(size); }
98 void *PsaiRealloc(void *ptr, SIZE_T size) { return realloc(ptr, size); }
99 void PsaiFree(void *ptr) { free(ptr); }
100
101 // Prototypes
102 unsigned int GetKeyPressed();
103
104 void GetInputOutputHandles()
105 {
106 #ifdef NEW_CONSOLE
107 HANDLE console = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
108 FILE_SHARE_READ | FILE_SHARE_WRITE,
109 0, CONSOLE_TEXTMODE_BUFFER, 0);
110
111 if (SetConsoleActiveScreenBuffer(console) == FALSE)
112 {
113 hStdin = GetStdHandle(STD_INPUT_HANDLE);
114 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
115 }
116 else
117 {
118 hStdin = GetStdHandle(STD_INPUT_HANDLE);//console;
119 hStdout = console;
120 }
121 #else
122 hStdin = GetStdHandle(STD_INPUT_HANDLE);
123 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
124 #endif
125 }
126
127 void RestoreConsole()
128 {
129 SetConsoleMode(hStdin, inConMode);
130 SetConsoleMode(hStdout, outConMode);
131
132 #ifdef NEW_CONSOLE
133 SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
134 #endif
135 }
136
137 void DisplayScreen()
138 {
139 COORD pos;
140 TCHAR lpStr[80];
141 DWORD numChars;
142 int lines;
143 int idx;
144
145 if (first == 0)
146 {
147 // Header
148 pos.X = 2; pos.Y = 2;
149 WriteConsoleOutputCharacter(hStdout, lpTitle, _tcslen(lpTitle), pos, &numChars);
150
151 pos.X = 2; pos.Y = 3;
152 WriteConsoleOutputCharacter(hStdout, lpSeparator, _tcslen(lpSeparator), pos, &numChars);
153
154 pos.X = 2; pos.Y = 4;
155 WriteConsoleOutputCharacter(hStdout, lpHeader, _tcslen(lpHeader), pos, &numChars);
156
157 pos.X = 2; pos.Y = 5;
158 WriteConsoleOutputCharacter(hStdout, lpSeparator, _tcslen(lpSeparator), pos, &numChars);
159
160 // Footer
161 pos.X = 2; pos.Y = ProcPerScreen+6;
162 WriteConsoleOutputCharacter(hStdout, lpSeparator, _tcslen(lpSeparator), pos, &numChars);
163
164 // Menu
165 pos.X = 2; pos.Y = ProcPerScreen+7;
166 WriteConsoleOutputCharacter(hStdout, lpEmpty, _tcslen(lpEmpty), pos, &numChars);
167 WriteConsoleOutputCharacter(hStdout, lpMenu, _tcslen(lpMenu), pos, &numChars);
168
169 first = 1;
170 }
171
172 // Processess
173 lines = ProcessCount;
174 if (lines > MAX_PROC)
175 lines = MAX_PROC;
176 for (idx=0; idx<MAX_PROC; idx++)
177 {
178 int len, i;
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 Idx, Idx2;
353 PSYSTEM_PROCESS_INFORMATION pSPI;
354 PPERFDATA pPDOld;
355 #ifdef EXTRA_INFO
356 HANDLE hProcess;
357 HANDLE hProcessToken;
358 TCHAR szTemp[MAX_PATH];
359 DWORD dwSize;
360 #endif
361 #ifdef TIMES
362 LARGE_INTEGER liCurrentKernelTime;
363 LARGE_INTEGER liCurrentIdleTime;
364 LARGE_INTEGER liCurrentTime;
365 #endif
366 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SysProcessorTimeInfo;
367 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo;
368
369 #ifdef TIMES
370 // Get new system time
371 status = NtQuerySystemInformation(SystemTimeInformation, &SysTimeInfo, sizeof(SysTimeInfo), 0);
372 if (status != NO_ERROR)
373 return;
374 #endif
375 // Get processor information
376 SysProcessorTimeInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)malloc(sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * SystemBasicInfo.NumberOfProcessors);
377 status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, SysProcessorTimeInfo, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * SystemBasicInfo.NumberOfProcessors, &ulSize);
378
379
380 // Get process information
381 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESS_INFORMATION *)&pBuffer);
382
383 #ifdef TIMES
384 liCurrentKernelTime.QuadPart = 0;
385 liCurrentIdleTime.QuadPart = 0;
386 for (Idx=0; Idx<SystemBasicInfo.NumberOfProcessors; Idx++) {
387 liCurrentKernelTime.QuadPart += SysProcessorTimeInfo[Idx].KernelTime.QuadPart;
388 liCurrentKernelTime.QuadPart += SysProcessorTimeInfo[Idx].DpcTime.QuadPart;
389 liCurrentKernelTime.QuadPart += SysProcessorTimeInfo[Idx].InterruptTime.QuadPart;
390 liCurrentIdleTime.QuadPart += SysProcessorTimeInfo[Idx].IdleTime.QuadPart;
391 }
392
393 // If it's a first call - skip idle time calcs
394 if (liOldIdleTime.QuadPart != 0) {
395 // CurrentValue = NewValue - OldValue
396 liCurrentTime.QuadPart = liCurrentIdleTime.QuadPart - liOldIdleTime.QuadPart;
397 dbIdleTime = Li2Double(liCurrentTime);
398 liCurrentTime.QuadPart = liCurrentKernelTime.QuadPart - liOldKernelTime.QuadPart;
399 dbKernelTime = Li2Double(liCurrentTime);
400 liCurrentTime.QuadPart = SysTimeInfo.CurrentTime.QuadPart - liOldSystemTime.QuadPart;
401 dbSystemTime = Li2Double(liCurrentTime);
402
403 // CurrentCpuIdle = IdleTime / SystemTime
404 dbIdleTime = dbIdleTime / dbSystemTime;
405 dbKernelTime = dbKernelTime / dbSystemTime;
406
407 // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
408 dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors;// + 0.5;
409 dbKernelTime = 100.0 - dbKernelTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors;// + 0.5;
410 }
411
412 // Store new CPU's idle and system time
413 liOldIdleTime = liCurrentIdleTime;
414 liOldSystemTime = SysTimeInfo.CurrentTime;
415 liOldKernelTime = liCurrentKernelTime;
416 #endif
417
418 // Determine the process count
419 // We loop through the data we got from PsaCaptureProcessesAndThreads
420 // and count how many structures there are (until PsaWalkNextProcess
421 // returns NULL)
422 ProcessCountOld = ProcessCount;
423 ProcessCount = 0;
424 pSPI = PsaWalkFirstProcess((PSYSTEM_PROCESS_INFORMATION)pBuffer);
425 while (pSPI) {
426 ProcessCount++;
427 pSPI = PsaWalkNextProcess(pSPI);
428 }
429
430 // Now alloc a new PERFDATA array and fill in the data
431 if (pPerfDataOld) {
432 free(pPerfDataOld);
433 }
434 pPerfDataOld = pPerfData;
435 pPerfData = (PPERFDATA)malloc(sizeof(PERFDATA) * ProcessCount);
436 pSPI = PsaWalkFirstProcess((PSYSTEM_PROCESS_INFORMATION)pBuffer);
437 for (Idx=0; Idx<ProcessCount; Idx++) {
438 // Get the old perf data for this process (if any)
439 // so that we can establish delta values
440 pPDOld = NULL;
441 for (Idx2=0; Idx2<ProcessCountOld; Idx2++) {
442 if (pPerfDataOld[Idx2].ProcessId == (ULONG)(pSPI->UniqueProcessId) &&
443 /* check also for the creation time, a new process may have an id of an old one */
444 pPerfDataOld[Idx2].CreateTime.QuadPart == pSPI->CreateTime.QuadPart) {
445 pPDOld = &pPerfDataOld[Idx2];
446 break;
447 }
448 }
449
450 // Clear out process perf data structure
451 memset(&pPerfData[Idx], 0, sizeof(PERFDATA));
452
453 if (pSPI->ImageName.Buffer) {
454 wcsncpy(pPerfData[Idx].ImageName, pSPI->ImageName.Buffer, pSPI->ImageName.Length / sizeof(WCHAR));
455 pPerfData[Idx].ImageName[pSPI->ImageName.Length / sizeof(WCHAR)] = 0;
456 }
457 else
458 {
459 #ifdef _UNICODE
460 wcscpy(pPerfData[Idx].ImageName, lpIdleProcess);
461 #else
462 MultiByteToWideChar(CP_ACP, 0, lpIdleProcess, strlen(lpIdleProcess), pPerfData[Idx].ImageName, MAX_PATH);
463 #endif
464 }
465
466 pPerfData[Idx].ProcessId = (ULONG)(pSPI->UniqueProcessId);
467 pPerfData[Idx].CreateTime = pSPI->CreateTime;
468
469 if (pPDOld) {
470 #ifdef TIMES
471 double CurTime = Li2Double(pSPI->KernelTime) + Li2Double(pSPI->UserTime);
472 double OldTime = Li2Double(pPDOld->KernelTime) + Li2Double(pPDOld->UserTime);
473 double CpuTime = (CurTime - OldTime) / dbSystemTime;
474 CpuTime = CpuTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors; // + 0.5;
475
476 pPerfData[Idx].CPUUsage = (ULONG)CpuTime;
477 #else
478 pPerfData[Idx].CPUUsage = 0;
479 #endif
480 }
481
482 pPerfData[Idx].CPUTime.QuadPart = pSPI->UserTime.QuadPart + pSPI->KernelTime.QuadPart;
483 pPerfData[Idx].WorkingSetSizeBytes = pSPI->WorkingSetSize;
484 pPerfData[Idx].PeakWorkingSetSizeBytes = pSPI->PeakWorkingSetSize;
485 if (pPDOld)
486 pPerfData[Idx].WorkingSetSizeDelta = labs((LONG)pSPI->WorkingSetSize - (LONG)pPDOld->WorkingSetSizeBytes);
487 else
488 pPerfData[Idx].WorkingSetSizeDelta = 0;
489 pPerfData[Idx].PageFaultCount = pSPI->PageFaultCount;
490 if (pPDOld)
491 pPerfData[Idx].PageFaultCountDelta = labs((LONG)pSPI->PageFaultCount - (LONG)pPDOld->PageFaultCount);
492 else
493 pPerfData[Idx].PageFaultCountDelta = 0;
494 pPerfData[Idx].VirtualMemorySizeBytes = pSPI->VirtualSize;
495 pPerfData[Idx].PagedPoolUsagePages = pSPI->QuotaPagedPoolUsage;
496 pPerfData[Idx].NonPagedPoolUsagePages = pSPI->QuotaNonPagedPoolUsage;
497 pPerfData[Idx].BasePriority = pSPI->BasePriority;
498 pPerfData[Idx].HandleCount = pSPI->HandleCount;
499 pPerfData[Idx].ThreadCount = pSPI->NumberOfThreads;
500 //pPerfData[Idx].SessionId = pSPI->SessionId;
501
502 #ifdef EXTRA_INFO
503 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pSPI->UniqueProcessId);
504 if (hProcess) {
505 if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_IMPERSONATE, &hProcessToken)) {
506 ImpersonateLoggedOnUser(hProcessToken);
507 memset(szTemp, 0, sizeof(TCHAR[MAX_PATH]));
508 dwSize = MAX_PATH;
509 GetUserName(szTemp, &dwSize);
510 #ifndef UNICODE
511 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTemp, -1, pPerfData[Idx].UserName, MAX_PATH);
512 #endif
513 RevertToSelf();
514 CloseHandle(hProcessToken);
515 }
516 CloseHandle(hProcess);
517 }
518 #endif
519 #ifdef TIMES
520 pPerfData[Idx].UserTime.QuadPart = pSPI->UserTime.QuadPart;
521 pPerfData[Idx].KernelTime.QuadPart = pSPI->KernelTime.QuadPart;
522 #endif
523 pSPI = PsaWalkNextProcess(pSPI);
524 }
525 PsaFreeCapture(pBuffer);
526
527 free(SysProcessorTimeInfo);
528 }
529
530 // Code partly taken from slw32tty.c from mc/slang
531 unsigned int GetKeyPressed(int events)
532 {
533 DWORD bytesRead;
534 INPUT_RECORD record;
535 int i;
536
537
538 for (i=0; i<events; i++)
539 {
540 if (!ReadConsoleInput(hStdin, &record, 1, &bytesRead)) {
541 return 0;
542 }
543
544 if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
545 return record.Event.KeyEvent.wVirtualKeyCode;//.uChar.AsciiChar;
546 }
547
548 return 0;
549 }
550
551
552 int main(int argc, char **argv)
553 {
554 int i;
555 TCHAR lpStr[80];
556
557 for (i = 0; i < 80; i++)
558 lpEmpty[i] = lpHeader[i] = _T(' ');
559 lpEmpty[79] = _T('\0');
560
561 /* Initialize global variables */
562 hInst = 0 /* FIXME: which value? [used with LoadString(hInst, ..., ..., ...)] */;
563 if (LoadString(hInst, IDS_COLUMN_IMAGENAME, lpStr, 80))
564 {
565 columnRightPositions[0] = _tcslen(lpStr);
566 _tcsncpy(&lpHeader[2], lpStr, _tcslen(lpStr));
567 }
568 if (LoadString(hInst, IDS_COLUMN_PID, lpStr, 80))
569 {
570 columnRightPositions[1] = columnRightPositions[0] + _tcslen(lpStr) + 3;
571 _tcsncpy(&lpHeader[columnRightPositions[0] + 2], lpStr, _tcslen(lpStr));
572 }
573 if (LoadString(hInst, IDS_COLUMN_CPU, lpStr, 80))
574 {
575 columnRightPositions[2] = columnRightPositions[1] + _tcslen(lpStr) + 3;
576 _tcsncpy(&lpHeader[columnRightPositions[1] + 2], lpStr, _tcslen(lpStr));
577 }
578 if (LoadString(hInst, IDS_COLUMN_MEM, lpStr, 80))
579 {
580 columnRightPositions[3] = columnRightPositions[2] + _tcslen(lpStr) + 3;
581 _tcsncpy(&lpHeader[columnRightPositions[2] + 2], lpStr, _tcslen(lpStr));
582 }
583 if (LoadString(hInst, IDS_COLUMN_PF, lpStr, 80))
584 {
585 columnRightPositions[4] = columnRightPositions[3] + _tcslen(lpStr) + 3;
586 _tcsncpy(&lpHeader[columnRightPositions[3] + 2], lpStr, _tcslen(lpStr));
587 }
588
589 for (i = 0; i < columnRightPositions[4]; i++)
590 lpSeparator[i] = _T('-');
591 lpHeader[0] = _T('|');
592 lpSeparator[0] = _T('+');
593 for (i = 0; i < 5; i++)
594 {
595 lpHeader[columnRightPositions[i]] = _T('|');
596 lpSeparator[columnRightPositions[i]] = _T('+');
597 }
598 lpSeparator[columnRightPositions[4] + 1] = _T('\0');
599 lpHeader[columnRightPositions[4] + 1] = _T('\0');
600
601
602 if (!LoadString(hInst, IDS_APP_TITLE, lpTitle, 80))
603 lpTitle[0] = _T('\0');
604 if (!LoadString(hInst, IDS_COLUMN_MEM_UNIT, lpMemUnit, 3))
605 lpMemUnit[0] = _T('\0');
606 if (!LoadString(hInst, IDS_MENU, lpMenu, 80))
607 lpMenu[0] = _T('\0');
608 if (!LoadString(hInst, IDS_IDLE_PROCESS, lpIdleProcess, 80))
609 lpIdleProcess[0] = _T('\0');
610
611 if (LoadString(hInst, IDS_MENU_QUIT, lpStr, 2))
612 KEY_QUIT = lpStr[0];
613 if (LoadString(hInst, IDS_MENU_KILL_PROCESS, lpStr, 2))
614 KEY_KILL = lpStr[0];
615 if (LoadString(hInst, IDS_YES, lpStr, 2))
616 KEY_YES = lpStr[0];
617 if (LoadString(hInst, IDS_NO, lpStr, 2))
618 KEY_NO = lpStr[0];
619
620 GetInputOutputHandles();
621
622 if (hStdin == INVALID_HANDLE_VALUE || hStdout == INVALID_HANDLE_VALUE)
623 {
624 if (LoadString(hInst, IDS_CTM_GENERAL_ERR1, lpStr, 80))
625 _tprintf(lpStr);
626 return -1;
627 }
628
629 if (GetConsoleMode(hStdin, &inConMode) == 0)
630 {
631 if (LoadString(hInst, IDS_CTM_GENERAL_ERR2, lpStr, 80))
632 _tprintf(lpStr);
633 return -1;
634 }
635
636 if (GetConsoleMode(hStdout, &outConMode) == 0)
637 {
638 if (LoadString(hInst, IDS_CTM_GENERAL_ERR3, lpStr, 80))
639 _tprintf(lpStr);
640 return -1;
641 }
642
643 SetConsoleMode(hStdin, 0); //FIXME: Should check for error!
644 SetConsoleMode(hStdout, 0); //FIXME: Should check for error!
645
646 PerfInit();
647
648 while (1)
649 {
650 DWORD numEvents;
651
652 PerfDataRefresh();
653 DisplayScreen();
654
655 /* WaitForSingleObject for console handles is not implemented in ROS */
656 WaitForSingleObject(hStdin, 1000);
657 GetNumberOfConsoleInputEvents(hStdin, &numEvents);
658
659 if (numEvents > 0)
660 {
661 if (ProcessKeys(numEvents) == TRUE)
662 break;
663 }
664 }
665
666 RestoreConsole();
667 return 0;
668 }