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
44 #define NTOS_MODE_USER
45 #include <ndk/ntndk.h>
47 #include <epsapi/epsapi.h>
62 DWORD columnRightPositions
[5];
63 TCHAR lpSeparator
[80];
66 TCHAR lpIdleProcess
[80];
72 TCHAR KEY_QUIT
, KEY_KILL
;
73 TCHAR KEY_YES
, KEY_NO
;
75 const int ProcPerScreen
= 17; // 17 processess are displayed on one page
76 ULONG ProcessCountOld
= 0;
77 ULONG ProcessCount
= 0;
82 LARGE_INTEGER liOldIdleTime
= {{0,0}};
83 LARGE_INTEGER liOldKernelTime
= {{0,0}};
84 LARGE_INTEGER liOldSystemTime
= {{0,0}};
86 PPERFDATA pPerfDataOld
= NULL
; // Older perf data (saved to establish delta values)
87 PPERFDATA pPerfData
= NULL
; // Most recent copy of perf data
90 int scrolled
=0; // offset from which process start showing
91 int first
= 0; // first time in DisplayScreen
92 SYSTEM_BASIC_INFORMATION SystemBasicInfo
;
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
); }
102 unsigned int GetKeyPressed();
104 void GetInputOutputHandles()
107 HANDLE console
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
108 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
109 0, CONSOLE_TEXTMODE_BUFFER
, 0);
111 if (SetConsoleActiveScreenBuffer(console
) == FALSE
)
113 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
114 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
118 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);//console;
122 hStdin
= GetStdHandle(STD_INPUT_HANDLE
);
123 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
127 void RestoreConsole()
129 SetConsoleMode(hStdin
, inConMode
);
130 SetConsoleMode(hStdout
, outConMode
);
133 SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE
));
149 pos
.X
= 2; pos
.Y
= 2;
150 WriteConsoleOutputCharacter(hStdout
, lpTitle
, _tcslen(lpTitle
), pos
, &numChars
);
152 pos
.X
= 2; pos
.Y
= 3;
153 WriteConsoleOutputCharacter(hStdout
, lpSeparator
, _tcslen(lpSeparator
), pos
, &numChars
);
155 pos
.X
= 2; pos
.Y
= 4;
156 WriteConsoleOutputCharacter(hStdout
, lpHeader
, _tcslen(lpHeader
), pos
, &numChars
);
158 pos
.X
= 2; pos
.Y
= 5;
159 WriteConsoleOutputCharacter(hStdout
, lpSeparator
, _tcslen(lpSeparator
), pos
, &numChars
);
162 pos
.X
= 2; pos
.Y
= ProcPerScreen
+6;
163 WriteConsoleOutputCharacter(hStdout
, lpSeparator
, _tcslen(lpSeparator
), pos
, &numChars
);
166 pos
.X
= 2; pos
.Y
= ProcPerScreen
+7;
167 WriteConsoleOutputCharacter(hStdout
, lpEmpty
, _tcslen(lpEmpty
), pos
, &numChars
);
168 WriteConsoleOutputCharacter(hStdout
, lpMenu
, _tcslen(lpMenu
), pos
, &numChars
);
174 lines
= ProcessCount
;
175 if (lines
> MAX_PROC
)
177 for (idx
=0; idx
<MAX_PROC
; idx
++)
180 TCHAR imgName
[MAX_PATH
];
184 TCHAR lpPageFaults
[15];
187 for (i
= 0; i
< 80; i
++)
191 if (idx
< lines
&& scrolled
+ idx
< ProcessCount
)
195 len
= wcslen(pPerfData
[scrolled
+idx
].ImageName
);
197 WideCharToMultiByte(CP_ACP
, 0, pPerfData
[scrolled
+idx
].ImageName
, -1,
198 imgName
, MAX_PATH
, NULL
, NULL
);
199 len
= strlen(imgName
);
201 if (len
> columnRightPositions
[0])
203 len
= columnRightPositions
[0];
206 wcsncpy(&lpStr
[2], pPerfData
[scrolled
+idx
].ImageName
, len
);
208 strncpy(&lpStr
[2], imgName
, len
);
212 _stprintf(lpPid
, _T("%6ld"), pPerfData
[scrolled
+idx
].ProcessId
);
213 _tcsncpy(&lpStr
[columnRightPositions
[1] - 6], lpPid
, 6);
217 _stprintf(lpCpu
, _T("%3d%%"), pPerfData
[scrolled
+idx
].CPUUsage
);
218 _tcsncpy(&lpStr
[columnRightPositions
[2] - 4], lpCpu
, 4);
222 _stprintf(lpMemUsg
, _T("%6ld %s"), pPerfData
[scrolled
+idx
].WorkingSetSizeBytes
/ 1024, lpMemUnit
);
223 _tcsncpy(&lpStr
[columnRightPositions
[3] - 9], lpMemUsg
, 9);
226 _stprintf(lpPageFaults
, _T("%12ld"), pPerfData
[scrolled
+idx
].PageFaultCount
);
227 _tcsncpy(&lpStr
[columnRightPositions
[4] - 12], lpPageFaults
, 12);
233 for (i
= 0; i
< 5; i
++)
234 lpStr
[columnRightPositions
[i
] + 1] = _T('|');
235 pos
.X
= 1; pos
.Y
= 6+idx
;
236 WriteConsoleOutputCharacter(hStdout
, lpStr
, 74, pos
, &numChars
);
239 pos
.X
= 3; pos
.Y
= 6+idx
;
240 if (selection
== idx
)
242 wColor
= BACKGROUND_GREEN
|
249 wColor
= BACKGROUND_BLUE
|
255 FillConsoleOutputAttribute(
256 hStdout
, // screen buffer handle
257 wColor
, // color to fill with
258 columnRightPositions
[0] - 1, // number of cells to fill
259 pos
, // first cell to write to
260 &numChars
); // actual number written
266 // returns TRUE if exiting
267 int ProcessKeys(int numEvents
)
270 if ((ProcessCount
-scrolled
< 17) && (ProcessCount
> 17))
271 scrolled
= ProcessCount
-17;
273 TCHAR key
= GetKeyPressed(numEvents
);
276 else if (key
== KEY_KILL
)
278 // user wants to kill some process, get his acknowledgement
283 pos
.X
= 2; pos
.Y
= 24;
284 if (LoadString(hInst
, IDS_KILL_PROCESS
, lpStr
, 100))
285 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
288 GetNumberOfConsoleInputEvents(hStdin
, &pId
);
289 key
= GetKeyPressed(pId
);
290 } while (key
!= KEY_YES
&& key
!= KEY_NO
);
295 pId
= pPerfData
[selection
+scrolled
].ProcessId
;
296 hProcess
= OpenProcess(PROCESS_TERMINATE
, FALSE
, pId
);
300 if (!TerminateProcess(hProcess
, 0))
302 if (LoadString(hInst
, IDS_KILL_PROCESS_ERR1
, lpStr
, 80))
304 WriteConsoleOutputCharacter(hStdout
, lpEmpty
, _tcslen(lpEmpty
), pos
, &numChars
);
305 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
310 CloseHandle(hProcess
);
314 if (LoadString(hInst
, IDS_KILL_PROCESS_ERR2
, lpStr
, 80))
316 WriteConsoleOutputCharacter(hStdout
, lpEmpty
, _tcslen(lpEmpty
), pos
, &numChars
);
317 _stprintf(lpStr
, lpStr
, pId
);
318 WriteConsoleOutputCharacter(hStdout
, lpStr
, _tcslen(lpStr
), pos
, &numChars
);
326 else if (key
== VK_UP
)
330 else if ((selection
== 0) && (scrolled
> 0))
333 else if (key
== VK_DOWN
)
335 if ((selection
< MAX_PROC
-1) && (selection
< ProcessCount
-1))
337 else if ((selection
== MAX_PROC
-1) && (selection
+scrolled
< ProcessCount
-1))
346 NtQuerySystemInformation(SystemBasicInformation
, &SystemBasicInfo
, sizeof(SystemBasicInfo
), 0);
349 void PerfDataRefresh()
357 HANDLE hProcessToken
;
358 PSYSTEM_PROCESS_INFORMATION pSPI
;
360 TCHAR szTemp
[MAX_PATH
];
363 LARGE_INTEGER liCurrentKernelTime
;
364 LARGE_INTEGER liCurrentIdleTime
;
365 LARGE_INTEGER liCurrentTime
;
367 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SysProcessorTimeInfo
;
368 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo
;
371 // Get new system time
372 status
= NtQuerySystemInformation(SystemTimeInformation
, &SysTimeInfo
, sizeof(SysTimeInfo
), 0);
373 if (status
!= NO_ERROR
)
376 // Get processor information
377 SysProcessorTimeInfo
= (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
)malloc(sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
) * SystemBasicInfo
.NumberOfProcessors
);
378 status
= NtQuerySystemInformation(SystemProcessorPerformanceInformation
, SysProcessorTimeInfo
, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
) * SystemBasicInfo
.NumberOfProcessors
, &ulSize
);
381 // Get process information
382 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESS_INFORMATION
*)&pBuffer
);
385 liCurrentKernelTime
.QuadPart
= 0;
386 liCurrentIdleTime
.QuadPart
= 0;
387 for (Idx
=0; Idx
<SystemBasicInfo
.NumberOfProcessors
; Idx
++) {
388 liCurrentKernelTime
.QuadPart
+= SysProcessorTimeInfo
[Idx
].KernelTime
.QuadPart
;
389 liCurrentKernelTime
.QuadPart
+= SysProcessorTimeInfo
[Idx
].DpcTime
.QuadPart
;
390 liCurrentKernelTime
.QuadPart
+= SysProcessorTimeInfo
[Idx
].InterruptTime
.QuadPart
;
391 liCurrentIdleTime
.QuadPart
+= SysProcessorTimeInfo
[Idx
].IdleTime
.QuadPart
;
394 // If it's a first call - skip idle time calcs
395 if (liOldIdleTime
.QuadPart
!= 0) {
396 // CurrentValue = NewValue - OldValue
397 liCurrentTime
.QuadPart
= liCurrentIdleTime
.QuadPart
- liOldIdleTime
.QuadPart
;
398 dbIdleTime
= Li2Double(liCurrentTime
);
399 liCurrentTime
.QuadPart
= liCurrentKernelTime
.QuadPart
- liOldKernelTime
.QuadPart
;
400 dbKernelTime
= Li2Double(liCurrentTime
);
401 liCurrentTime
.QuadPart
= SysTimeInfo
.CurrentTime
.QuadPart
- liOldSystemTime
.QuadPart
;
402 dbSystemTime
= Li2Double(liCurrentTime
);
404 // CurrentCpuIdle = IdleTime / SystemTime
405 dbIdleTime
= dbIdleTime
/ dbSystemTime
;
406 dbKernelTime
= dbKernelTime
/ dbSystemTime
;
408 // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
409 dbIdleTime
= 100.0 - dbIdleTime
* 100.0 / (double)SystemBasicInfo
.NumberOfProcessors
;// + 0.5;
410 dbKernelTime
= 100.0 - dbKernelTime
* 100.0 / (double)SystemBasicInfo
.NumberOfProcessors
;// + 0.5;
413 // Store new CPU's idle and system time
414 liOldIdleTime
= liCurrentIdleTime
;
415 liOldSystemTime
= SysTimeInfo
.CurrentTime
;
416 liOldKernelTime
= liCurrentKernelTime
;
419 // Determine the process count
420 // We loop through the data we got from PsaCaptureProcessesAndThreads
421 // and count how many structures there are (until PsaWalkNextProcess
423 ProcessCountOld
= ProcessCount
;
425 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESS_INFORMATION
)pBuffer
);
428 pSPI
= PsaWalkNextProcess(pSPI
);
431 // Now alloc a new PERFDATA array and fill in the data
435 pPerfDataOld
= pPerfData
;
436 pPerfData
= (PPERFDATA
)malloc(sizeof(PERFDATA
) * ProcessCount
);
437 pSPI
= PsaWalkFirstProcess((PSYSTEM_PROCESS_INFORMATION
)pBuffer
);
438 for (Idx
=0; Idx
<ProcessCount
; Idx
++) {
439 // Get the old perf data for this process (if any)
440 // so that we can establish delta values
442 for (Idx2
=0; Idx2
<ProcessCountOld
; Idx2
++) {
443 if (pPerfDataOld
[Idx2
].ProcessId
== (ULONG
)(pSPI
->UniqueProcessId
) &&
444 /* check also for the creation time, a new process may have an id of an old one */
445 pPerfDataOld
[Idx2
].CreateTime
.QuadPart
== pSPI
->CreateTime
.QuadPart
) {
446 pPDOld
= &pPerfDataOld
[Idx2
];
451 // Clear out process perf data structure
452 memset(&pPerfData
[Idx
], 0, sizeof(PERFDATA
));
454 if (pSPI
->ImageName
.Buffer
) {
455 wcsncpy(pPerfData
[Idx
].ImageName
, pSPI
->ImageName
.Buffer
, pSPI
->ImageName
.Length
/ sizeof(WCHAR
));
456 pPerfData
[Idx
].ImageName
[pSPI
->ImageName
.Length
/ sizeof(WCHAR
)] = 0;
461 wcscpy(pPerfData
[Idx
].ImageName
, lpIdleProcess
);
463 MultiByteToWideChar(CP_ACP
, 0, lpIdleProcess
, strlen(lpIdleProcess
), pPerfData
[Idx
].ImageName
, MAX_PATH
);
467 pPerfData
[Idx
].ProcessId
= (ULONG
)(pSPI
->UniqueProcessId
);
468 pPerfData
[Idx
].CreateTime
= pSPI
->CreateTime
;
472 double CurTime
= Li2Double(pSPI
->KernelTime
) + Li2Double(pSPI
->UserTime
);
473 double OldTime
= Li2Double(pPDOld
->KernelTime
) + Li2Double(pPDOld
->UserTime
);
474 double CpuTime
= (CurTime
- OldTime
) / dbSystemTime
;
475 CpuTime
= CpuTime
* 100.0 / (double)SystemBasicInfo
.NumberOfProcessors
; // + 0.5;
477 pPerfData
[Idx
].CPUUsage
= (ULONG
)CpuTime
;
479 pPerfData
[Idx
].CPUUsage
= 0;
483 pPerfData
[Idx
].CPUTime
.QuadPart
= pSPI
->UserTime
.QuadPart
+ pSPI
->KernelTime
.QuadPart
;
484 pPerfData
[Idx
].WorkingSetSizeBytes
= pSPI
->WorkingSetSize
;
485 pPerfData
[Idx
].PeakWorkingSetSizeBytes
= pSPI
->PeakWorkingSetSize
;
487 pPerfData
[Idx
].WorkingSetSizeDelta
= labs((LONG
)pSPI
->WorkingSetSize
- (LONG
)pPDOld
->WorkingSetSizeBytes
);
489 pPerfData
[Idx
].WorkingSetSizeDelta
= 0;
490 pPerfData
[Idx
].PageFaultCount
= pSPI
->PageFaultCount
;
492 pPerfData
[Idx
].PageFaultCountDelta
= labs((LONG
)pSPI
->PageFaultCount
- (LONG
)pPDOld
->PageFaultCount
);
494 pPerfData
[Idx
].PageFaultCountDelta
= 0;
495 pPerfData
[Idx
].VirtualMemorySizeBytes
= pSPI
->VirtualSize
;
496 pPerfData
[Idx
].PagedPoolUsagePages
= pSPI
->QuotaPagedPoolUsage
;
497 pPerfData
[Idx
].NonPagedPoolUsagePages
= pSPI
->QuotaNonPagedPoolUsage
;
498 pPerfData
[Idx
].BasePriority
= pSPI
->BasePriority
;
499 pPerfData
[Idx
].HandleCount
= pSPI
->HandleCount
;
500 pPerfData
[Idx
].ThreadCount
= pSPI
->NumberOfThreads
;
501 //pPerfData[Idx].SessionId = pSPI->SessionId;
504 hProcess
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, pSPI
->UniqueProcessId
);
506 if (OpenProcessToken(hProcess
, TOKEN_QUERY
|TOKEN_DUPLICATE
|TOKEN_IMPERSONATE
, &hProcessToken
)) {
507 ImpersonateLoggedOnUser(hProcessToken
);
508 memset(szTemp
, 0, sizeof(TCHAR
[MAX_PATH
]));
510 GetUserName(szTemp
, &dwSize
);
512 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, szTemp
, -1, pPerfData
[Idx
].UserName
, MAX_PATH
);
514 int MultiByteToWideChar(
515 UINT CodePage, // code page
516 DWORD dwFlags, // character-type options
517 LPCSTR lpMultiByteStr, // string to map
518 int cbMultiByte, // number of bytes in string
519 LPWSTR lpWideCharStr, // wide-character buffer
520 int cchWideChar // size of buffer
525 CloseHandle(hProcessToken
);
527 CloseHandle(hProcess
);
531 pPerfData
[Idx
].UserTime
.QuadPart
= pSPI
->UserTime
.QuadPart
;
532 pPerfData
[Idx
].KernelTime
.QuadPart
= pSPI
->KernelTime
.QuadPart
;
534 pSPI
= PsaWalkNextProcess(pSPI
);
536 PsaFreeCapture(pBuffer
);
538 free(SysProcessorTimeInfo
);
541 // Code partly taken from slw32tty.c from mc/slang
542 unsigned int GetKeyPressed(int events
)
550 for (i
=0; i
<events
; i
++)
552 if (!ReadConsoleInput(hStdin
, &record
, 1, &bytesRead
)) {
556 if (record
.EventType
== KEY_EVENT
&& record
.Event
.KeyEvent
.bKeyDown
)
557 return record
.Event
.KeyEvent
.wVirtualKeyCode
;//.uChar.AsciiChar;
564 int main(int argc
, char **argv
)
569 for (i
= 0; i
< 80; i
++)
570 lpEmpty
[i
] = lpHeader
[i
] = _T(' ');
571 lpEmpty
[79] = _T('\0');
573 /* Initialize global variables */
574 hInst
= 0 /* FIXME: which value? [used with LoadString(hInst, ..., ..., ...)] */;
575 if (LoadString(hInst
, IDS_COLUMN_IMAGENAME
, lpStr
, 80))
577 columnRightPositions
[0] = _tcslen(lpStr
);
578 _tcsncpy(&lpHeader
[2], lpStr
, _tcslen(lpStr
));
580 if (LoadString(hInst
, IDS_COLUMN_PID
, lpStr
, 80))
582 columnRightPositions
[1] = columnRightPositions
[0] + _tcslen(lpStr
) + 3;
583 _tcsncpy(&lpHeader
[columnRightPositions
[0] + 2], lpStr
, _tcslen(lpStr
));
585 if (LoadString(hInst
, IDS_COLUMN_CPU
, lpStr
, 80))
587 columnRightPositions
[2] = columnRightPositions
[1] + _tcslen(lpStr
) + 3;
588 _tcsncpy(&lpHeader
[columnRightPositions
[1] + 2], lpStr
, _tcslen(lpStr
));
590 if (LoadString(hInst
, IDS_COLUMN_MEM
, lpStr
, 80))
592 columnRightPositions
[3] = columnRightPositions
[2] + _tcslen(lpStr
) + 3;
593 _tcsncpy(&lpHeader
[columnRightPositions
[2] + 2], lpStr
, _tcslen(lpStr
));
595 if (LoadString(hInst
, IDS_COLUMN_PF
, lpStr
, 80))
597 columnRightPositions
[4] = columnRightPositions
[3] + _tcslen(lpStr
) + 3;
598 _tcsncpy(&lpHeader
[columnRightPositions
[3] + 2], lpStr
, _tcslen(lpStr
));
601 for (i
= 0; i
< columnRightPositions
[4]; i
++)
602 lpSeparator
[i
] = _T('-');
603 lpHeader
[0] = _T('|');
604 lpSeparator
[0] = _T('+');
605 for (i
= 0; i
< 5; i
++)
607 lpHeader
[columnRightPositions
[i
]] = _T('|');
608 lpSeparator
[columnRightPositions
[i
]] = _T('+');
610 lpSeparator
[columnRightPositions
[4] + 1] = _T('\0');
611 lpHeader
[columnRightPositions
[4] + 1] = _T('\0');
614 if (!LoadString(hInst
, IDS_APP_TITLE
, lpTitle
, 80))
615 lpTitle
[0] = _T('\0');
616 if (!LoadString(hInst
, IDS_COLUMN_MEM_UNIT
, lpMemUnit
, 3))
617 lpMemUnit
[0] = _T('\0');
618 if (!LoadString(hInst
, IDS_MENU
, lpMenu
, 80))
619 lpMenu
[0] = _T('\0');
620 if (!LoadString(hInst
, IDS_IDLE_PROCESS
, lpIdleProcess
, 80))
621 lpIdleProcess
[0] = _T('\0');
623 if (LoadString(hInst
, IDS_MENU_QUIT
, lpStr
, 2))
625 if (LoadString(hInst
, IDS_MENU_KILL_PROCESS
, lpStr
, 2))
627 if (LoadString(hInst
, IDS_YES
, lpStr
, 2))
629 if (LoadString(hInst
, IDS_NO
, lpStr
, 2))
632 GetInputOutputHandles();
634 if (hStdin
== INVALID_HANDLE_VALUE
|| hStdout
== INVALID_HANDLE_VALUE
)
636 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR1
, lpStr
, 80))
641 if (GetConsoleMode(hStdin
, &inConMode
) == 0)
643 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR2
, lpStr
, 80))
648 if (GetConsoleMode(hStdout
, &outConMode
) == 0)
650 if (LoadString(hInst
, IDS_CTM_GENERAL_ERR3
, lpStr
, 80))
655 SetConsoleMode(hStdin
, 0); //FIXME: Should check for error!
656 SetConsoleMode(hStdout
, 0); //FIXME: Should check for error!
667 /* WaitForSingleObject for console handles is not implemented in ROS */
668 WaitForSingleObject(hStdin
, 1000);
669 GetNumberOfConsoleInputEvents(hStdin
, &numEvents
);
673 if (ProcessKeys(numEvents
) == TRUE
)