- Fixed some minor bugs.
[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 double OldKernelTime = 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_PROCESSES pSPI;
357 PPERFDATA pPDOld;
358 TCHAR szTemp[MAX_PATH];
359 DWORD dwSize;
360 double CurrentKernelTime;
361 PSYSTEM_PROCESSORTIME_INFO SysProcessorTimeInfo;
362 SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
363 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo;
364
365 #ifdef TIMES
366 // Get new system time
367 status = NtQuerySystemInformation(SystemTimeInformation, &SysTimeInfo, sizeof(SysTimeInfo), 0);
368 if (status != NO_ERROR)
369 return;
370
371 // Get new CPU's idle time
372 status = NtQuerySystemInformation(SystemPerformanceInformation, &SysPerfInfo, sizeof(SysPerfInfo), NULL);
373 if (status != NO_ERROR)
374 return;
375 #endif
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);
379
380
381 // Get process information
382 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESSES *)&pBuffer);
383
384 #ifdef TIMES
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);
389 }
390
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);
397
398 // CurrentCpuIdle = IdleTime / SystemTime
399 dbIdleTime = dbIdleTime / dbSystemTime;
400 dbKernelTime = dbKernelTime / dbSystemTime;
401
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;
405 }
406
407 // Store new CPU's idle and system time
408 liOldIdleTime = SysPerfInfo.IdleTime;
409 liOldSystemTime = SysTimeInfo.CurrentTime;
410 OldKernelTime = CurrentKernelTime;
411 #endif
412
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
416 // returns NULL)
417 ProcessCountOld = ProcessCount;
418 ProcessCount = 0;
419 pSPI = PsaWalkFirstProcess((PSYSTEM_PROCESSES)pBuffer);
420 while (pSPI) {
421 ProcessCount++;
422 pSPI = PsaWalkNextProcess(pSPI);
423 }
424
425 // Now alloc a new PERFDATA array and fill in the data
426 if (pPerfDataOld) {
427 free(pPerfDataOld);
428 }
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
435 pPDOld = NULL;
436 for (Idx2=0; Idx2<ProcessCountOld; Idx2++) {
437 if (pPerfDataOld[Idx2].ProcessId == pSPI->ProcessId) {
438 pPDOld = &pPerfDataOld[Idx2];
439 break;
440 }
441 }
442
443 // Clear out process perf data structure
444 memset(&pPerfData[Idx], 0, sizeof(PERFDATA));
445
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;
449 }
450 else
451 {
452 #ifdef _UNICODE
453 wcscpy(pPerfData[Idx].ImageName, lpIdleProcess);
454 #else
455 MultiByteToWideChar(CP_ACP, 0, lpIdleProcess, strlen(lpIdleProcess), pPerfData[Idx].ImageName, MAX_PATH);
456 #endif
457 }
458
459 pPerfData[Idx].ProcessId = pSPI->ProcessId;
460
461 if (pPDOld) {
462 #ifdef TIMES
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;
467
468 pPerfData[Idx].CPUUsage = (ULONG)CpuTime;
469 #else
470 pPerfData[Idx].CPUUsage = 0;
471 #endif
472 }
473
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;
477 if (pPDOld)
478 pPerfData[Idx].WorkingSetSizeDelta = labs((LONG)pSPI->VmCounters.WorkingSetSize - (LONG)pPDOld->WorkingSetSizeBytes);
479 else
480 pPerfData[Idx].WorkingSetSizeDelta = 0;
481 pPerfData[Idx].PageFaultCount = pSPI->VmCounters.PageFaultCount;
482 if (pPDOld)
483 pPerfData[Idx].PageFaultCountDelta = labs((LONG)pSPI->VmCounters.PageFaultCount - (LONG)pPDOld->PageFaultCount);
484 else
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;
493
494 #ifdef EXTRA_INFO
495 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pSPI->ProcessId);
496 if (hProcess) {
497 if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_IMPERSONATE, &hProcessToken)) {
498 ImpersonateLoggedOnUser(hProcessToken);
499 memset(szTemp, 0, sizeof(TCHAR[MAX_PATH]));
500 dwSize = MAX_PATH;
501 GetUserName(szTemp, &dwSize);
502 #ifndef UNICODE
503 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTemp, -1, pPerfData[Idx].UserName, MAX_PATH);
504 /*
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
512 );
513 */
514 #endif
515 RevertToSelf();
516 CloseHandle(hProcessToken);
517 }
518 CloseHandle(hProcess);
519 }
520 #endif
521 #ifdef TIMES
522 pPerfData[Idx].UserTime.QuadPart = pSPI->UserTime.QuadPart;
523 pPerfData[Idx].KernelTime.QuadPart = pSPI->KernelTime.QuadPart;
524 #endif
525 pSPI = PsaWalkNextProcess(pSPI);
526 }
527 PsaFreeCapture(pBuffer);
528
529 free(SysProcessorTimeInfo);
530 }
531
532 // Code partly taken from slw32tty.c from mc/slang
533 unsigned int GetKeyPressed(int events)
534 {
535 long key;
536 DWORD bytesRead;
537 INPUT_RECORD record;
538 int i;
539
540
541 for (i=0; i<events; i++)
542 {
543 if (!ReadConsoleInput(hStdin, &record, 1, &bytesRead)) {
544 return 0;
545 }
546
547 if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
548 return record.Event.KeyEvent.wVirtualKeyCode;//.uChar.AsciiChar;
549 }
550
551 return 0;
552 }
553
554
555 int main(int *argc, char **argv)
556 {
557 int i;
558 TCHAR lpStr[80];
559
560 for (i = 0; i < 80; i++)
561 lpEmpty[i] = lpHeader[i] = _T(' ');
562 lpEmpty[79] = _T('\0');
563
564 /* Initialize global variables */
565 hInst = 0 /* FIXME: which value? [used with LoadString(hInst, ..., ..., ...)] */;
566 if (LoadString(hInst, IDS_COLUMN_IMAGENAME, lpStr, 80))
567 {
568 columnRightPositions[0] = _tcslen(lpStr);
569 _tcsncpy(&lpHeader[2], lpStr, _tcslen(lpStr));
570 }
571 if (LoadString(hInst, IDS_COLUMN_PID, lpStr, 80))
572 {
573 columnRightPositions[1] = columnRightPositions[0] + _tcslen(lpStr) + 3;
574 _tcsncpy(&lpHeader[columnRightPositions[0] + 2], lpStr, _tcslen(lpStr));
575 }
576 if (LoadString(hInst, IDS_COLUMN_CPU, lpStr, 80))
577 {
578 columnRightPositions[2] = columnRightPositions[1] + _tcslen(lpStr) + 3;
579 _tcsncpy(&lpHeader[columnRightPositions[1] + 2], lpStr, _tcslen(lpStr));
580 }
581 if (LoadString(hInst, IDS_COLUMN_MEM, lpStr, 80))
582 {
583 columnRightPositions[3] = columnRightPositions[2] + _tcslen(lpStr) + 3;
584 _tcsncpy(&lpHeader[columnRightPositions[2] + 2], lpStr, _tcslen(lpStr));
585 }
586 if (LoadString(hInst, IDS_COLUMN_PF, lpStr, 80))
587 {
588 columnRightPositions[4] = columnRightPositions[3] + _tcslen(lpStr) + 3;
589 _tcsncpy(&lpHeader[columnRightPositions[3] + 2], lpStr, _tcslen(lpStr));
590 }
591
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++)
597 {
598 lpHeader[columnRightPositions[i]] = _T('|');
599 lpSeparator[columnRightPositions[i]] = _T('+');
600 }
601 lpSeparator[columnRightPositions[4] + 1] = _T('\0');
602 lpHeader[columnRightPositions[4] + 1] = _T('\0');
603
604
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');
613
614 if (LoadString(hInst, IDS_MENU_QUIT, lpStr, 2))
615 KEY_QUIT = lpStr[0];
616 if (LoadString(hInst, IDS_MENU_KILL_PROCESS, lpStr, 2))
617 KEY_KILL = lpStr[0];
618 if (LoadString(hInst, IDS_YES, lpStr, 2))
619 KEY_YES = lpStr[0];
620 if (LoadString(hInst, IDS_NO, lpStr, 2))
621 KEY_NO = lpStr[0];
622
623 GetInputOutputHandles();
624
625 if (hStdin == INVALID_HANDLE_VALUE || hStdout == INVALID_HANDLE_VALUE)
626 {
627 if (LoadString(hInst, IDS_CTM_GENERAL_ERR1, lpStr, 80))
628 _tprintf(lpStr);
629 return -1;
630 }
631
632 if (GetConsoleMode(hStdin, &inConMode) == 0)
633 {
634 if (LoadString(hInst, IDS_CTM_GENERAL_ERR2, lpStr, 80))
635 _tprintf(lpStr);
636 return -1;
637 }
638
639 if (GetConsoleMode(hStdout, &outConMode) == 0)
640 {
641 if (LoadString(hInst, IDS_CTM_GENERAL_ERR3, lpStr, 80))
642 _tprintf(lpStr);
643 return -1;
644 }
645
646 SetConsoleMode(hStdin, 0); //FIXME: Should check for error!
647 SetConsoleMode(hStdout, 0); //FIXME: Should check for error!
648
649 PerfInit();
650
651 while (1)
652 {
653 DWORD numEvents;
654
655 PerfDataRefresh();
656 DisplayScreen();
657
658 /* WaitForSingleObject for console handles is not implemented in ROS */
659 WaitForSingleObject(hStdin, 1000);
660 GetNumberOfConsoleInputEvents(hStdin, &numEvents);
661
662 if (numEvents > 0)
663 {
664 if (ProcessKeys(numEvents) == TRUE)
665 break;
666 }
667 }
668
669 RestoreConsole();
670 return 0;
671 }