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
35 //#include <windows.h>
43 #include <ddk/ntddk.h>
44 #include <ndk/ntndk.h>
45 #include <epsapi/epsapi.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 LARGE_INTEGER liOldKernelTime
= {{0,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_PROCESS_INFORMATION pSPI
;
358 TCHAR szTemp
[MAX_PATH
];
361 LARGE_INTEGER liCurrentKernelTime
;
362 LARGE_INTEGER liCurrentIdleTime
;
363 LARGE_INTEGER liCurrentTime
;
365 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SysProcessorTimeInfo
;
366 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo
;
369 // Get new system time
370 status
= NtQuerySystemInformation(SystemTimeInformation
, &SysTimeInfo
, sizeof(SysTimeInfo
), 0);
371 if (status
!= NO_ERROR
)
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
);
379 // Get process information
380 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESS_INFORMATION
*)&pBuffer
);
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
;
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
);
402 // CurrentCpuIdle = IdleTime / SystemTime
403 dbIdleTime
= dbIdleTime
/ dbSystemTime
;
404 dbKernelTime
= dbKernelTime
/ dbSystemTime
;
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;
411 // Store new CPU's idle and system time
412 liOldIdleTime
= liCurrentIdleTime
;
413 liOldSystemTime
= SysTimeInfo
.CurrentTime
;
414 liOldKernelTime
= liCurrentKernelTime
;
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
421 ProcessCountOld
= ProcessCount
;
423 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESS_INFORMATION
)pBuffer
);
426 pSPI
= PsaWalkNextProcess(pSPI
);
429 // Now alloc a new PERFDATA array and fill in the data
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
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
];
449 // Clear out process perf data structure
450 memset(&pPerfData
[Idx
], 0, sizeof(PERFDATA
));
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;
459 wcscpy(pPerfData
[Idx
].ImageName
, lpIdleProcess
);
461 MultiByteToWideChar(CP_ACP
, 0, lpIdleProcess
, strlen(lpIdleProcess
), pPerfData
[Idx
].ImageName
, MAX_PATH
);
465 pPerfData
[Idx
].ProcessId
= (ULONG
)(pSPI
->UniqueProcessId
);
466 pPerfData
[Idx
].CreateTime
= pSPI
->CreateTime
;
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;
475 pPerfData
[Idx
].CPUUsage
= (ULONG
)CpuTime
;
477 pPerfData
[Idx
].CPUUsage
= 0;
481 pPerfData
[Idx
].CPUTime
.QuadPart
= pSPI
->UserTime
.QuadPart
+ pSPI
->KernelTime
.QuadPart
;
482 pPerfData
[Idx
].WorkingSetSizeBytes
= pSPI
->WorkingSetSize
;
483 pPerfData
[Idx
].PeakWorkingSetSizeBytes
= pSPI
->PeakWorkingSetSize
;
485 pPerfData
[Idx
].WorkingSetSizeDelta
= labs((LONG
)pSPI
->WorkingSetSize
- (LONG
)pPDOld
->WorkingSetSizeBytes
);
487 pPerfData
[Idx
].WorkingSetSizeDelta
= 0;
488 pPerfData
[Idx
].PageFaultCount
= pSPI
->PageFaultCount
;
490 pPerfData
[Idx
].PageFaultCountDelta
= labs((LONG
)pSPI
->PageFaultCount
- (LONG
)pPDOld
->PageFaultCount
);
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;
502 hProcess
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, pSPI
->UniqueProcessId
);
504 if (OpenProcessToken(hProcess
, TOKEN_QUERY
|TOKEN_DUPLICATE
|TOKEN_IMPERSONATE
, &hProcessToken
)) {
505 ImpersonateLoggedOnUser(hProcessToken
);
506 memset(szTemp
, 0, sizeof(TCHAR
[MAX_PATH
]));
508 GetUserName(szTemp
, &dwSize
);
510 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, szTemp
, -1, pPerfData
[Idx
].UserName
, MAX_PATH
);
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
523 CloseHandle(hProcessToken
);
525 CloseHandle(hProcess
);
529 pPerfData
[Idx
].UserTime
.QuadPart
= pSPI
->UserTime
.QuadPart
;
530 pPerfData
[Idx
].KernelTime
.QuadPart
= pSPI
->KernelTime
.QuadPart
;
532 pSPI
= PsaWalkNextProcess(pSPI
);
534 PsaFreeCapture(pBuffer
);
536 free(SysProcessorTimeInfo
);
539 // Code partly taken from slw32tty.c from mc/slang
540 unsigned int GetKeyPressed(int events
)
548 for (i
=0; i
<events
; i
++)
550 if (!ReadConsoleInput(hStdin
, &record
, 1, &bytesRead
)) {
554 if (record
.EventType
== KEY_EVENT
&& record
.Event
.KeyEvent
.bKeyDown
)
555 return record
.Event
.KeyEvent
.wVirtualKeyCode
;//.uChar.AsciiChar;
562 int main(int argc
, char **argv
)
567 for (i
= 0; i
< 80; i
++)
568 lpEmpty
[i
] = lpHeader
[i
] = _T(' ');
569 lpEmpty
[79] = _T('\0');
571 /* Initialize global variables */
572 hInst
= 0 /* FIXME: which value? [used with LoadString(hInst, ..., ..., ...)] */;
573 if (LoadString(hInst
, IDS_COLUMN_IMAGENAME
, lpStr
, 80))
575 columnRightPositions
[0] = _tcslen(lpStr
);
576 _tcsncpy(&lpHeader
[2], lpStr
, _tcslen(lpStr
));
578 if (LoadString(hInst
, IDS_COLUMN_PID
, lpStr
, 80))
580 columnRightPositions
[1] = columnRightPositions
[0] + _tcslen(lpStr
) + 3;
581 _tcsncpy(&lpHeader
[columnRightPositions
[0] + 2], lpStr
, _tcslen(lpStr
));
583 if (LoadString(hInst
, IDS_COLUMN_CPU
, lpStr
, 80))
585 columnRightPositions
[2] = columnRightPositions
[1] + _tcslen(lpStr
) + 3;
586 _tcsncpy(&lpHeader
[columnRightPositions
[1] + 2], lpStr
, _tcslen(lpStr
));
588 if (LoadString(hInst
, IDS_COLUMN_MEM
, lpStr
, 80))
590 columnRightPositions
[3] = columnRightPositions
[2] + _tcslen(lpStr
) + 3;
591 _tcsncpy(&lpHeader
[columnRightPositions
[2] + 2], lpStr
, _tcslen(lpStr
));
593 if (LoadString(hInst
, IDS_COLUMN_PF
, lpStr
, 80))
595 columnRightPositions
[4] = columnRightPositions
[3] + _tcslen(lpStr
) + 3;
596 _tcsncpy(&lpHeader
[columnRightPositions
[3] + 2], lpStr
, _tcslen(lpStr
));
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
++)
605 lpHeader
[columnRightPositions
[i
]] = _T('|');
606 lpSeparator
[columnRightPositions
[i
]] = _T('+');
608 lpSeparator
[columnRightPositions
[4] + 1] = _T('\0');
609 lpHeader
[columnRightPositions
[4] + 1] = _T('\0');
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');
621 if (LoadString(hInst
, IDS_MENU_QUIT
, lpStr
, 2))
623 if (LoadString(hInst
, IDS_MENU_KILL_PROCESS
, lpStr
, 2))
625 if (LoadString(hInst
, IDS_YES
, lpStr
, 2))
627 if (LoadString(hInst
, IDS_NO
, lpStr
, 2))
630 GetInputOutputHandles();
632 if (hStdin
== INVALID_HANDLE_VALUE
|| hStdout
== INVALID_HANDLE_VALUE
)
634 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR1
, lpStr
, 80))
639 if (GetConsoleMode(hStdin
, &inConMode
) == 0)
641 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR2
, lpStr
, 80))
646 if (GetConsoleMode(hStdout
, &outConMode
) == 0)
648 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR3
, lpStr
, 80))
653 SetConsoleMode(hStdin
, 0); //FIXME: Should check for error!
654 SetConsoleMode(hStdout
, 0); //FIXME: Should check for error!
665 /* WaitForSingleObject for console handles is not implemented in ROS */
666 WaitForSingleObject(hStdin
, 1000);
667 GetNumberOfConsoleInputEvents(hStdin
, &numEvents
);
671 if (ProcessKeys(numEvents
) == TRUE
)