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