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