[CRT] Massively improve performance of rand_s
[reactos.git] / base / applications / taskmgr / procpage.c
1 /*
2 * PROJECT: ReactOS Task Manager
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Processes Page
5 * COPYRIGHT: Copyright 1999-2001 Brian Palmer <brianp@reactos.org>
6 * Copyright 2009 Maxime Vernier <maxime.vernier@gmail.com>
7 * Copyright 2022 Thamatip Chitpong <tangaming123456@outlook.com>
8 */
9
10 #include "precomp.h"
11
12 #include "proclist.h"
13
14 #include <ndk/psfuncs.h>
15
16 #define CMP(x1, x2)\
17 (x1 < x2 ? -1 : (x1 > x2 ? 1 : 0))
18
19 #define CONST_STR_LEN(str) (_countof(str) - 1)
20
21 typedef struct
22 {
23 ULONG ProcessId;
24 } PROCESS_PAGE_LIST_ITEM, *LPPROCESS_PAGE_LIST_ITEM;
25
26 HWND hProcessPage; /* Process List Property Page */
27
28 HWND hProcessPageListCtrl; /* Process ListCtrl Window */
29 HWND hProcessPageHeaderCtrl; /* Process Header Control */
30 static HWND hProcessPageEndProcessButton; /* Process End Process button */
31 static HWND hProcessPageShowAllProcessesButton;/* Process Show All Processes checkbox */
32 BOOL bProcessPageSelectionMade = FALSE; /* Is item in ListCtrl selected */
33
34 static int nProcessPageWidth;
35 static int nProcessPageHeight;
36 #ifdef RUN_PROC_PAGE
37 static HANDLE hProcessThread = NULL;
38 static DWORD dwProcessThread;
39 #endif
40
41 int CALLBACK ProcessPageCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
42 void AddProcess(ULONG Index);
43 void UpdateProcesses();
44 void gethmsfromlargeint(LARGE_INTEGER largeint, DWORD *dwHours, DWORD *dwMinutes, DWORD *dwSeconds);
45 void ProcessPageOnNotify(WPARAM wParam, LPARAM lParam);
46 void ProcessPageShowContextMenu(DWORD dwProcessId);
47 BOOL PerfDataGetText(ULONG Index, ULONG ColumnIndex, LPTSTR lpText, ULONG nMaxCount);
48 DWORD WINAPI ProcessPageRefreshThread(void *lpParameter);
49 int ProcessRunning(ULONG ProcessId);
50
51 void Cleanup(void)
52 {
53 int i;
54 LV_ITEM item;
55 LPPROCESS_PAGE_LIST_ITEM pData;
56 for (i = 0; i < ListView_GetItemCount(hProcessPageListCtrl); i++)
57 {
58 memset(&item, 0, sizeof(LV_ITEM));
59 item.mask = LVIF_PARAM;
60 item.iItem = i;
61 (void)ListView_GetItem(hProcessPageListCtrl, &item);
62 pData = (LPPROCESS_PAGE_LIST_ITEM)item.lParam;
63 HeapFree(GetProcessHeap(), 0, pData);
64 }
65 }
66
67 int ProcGetIndexByProcessId(DWORD dwProcessId)
68 {
69 int i;
70 LVITEM item;
71 LPPROCESS_PAGE_LIST_ITEM pData;
72
73 for (i=0; i<ListView_GetItemCount(hProcessPageListCtrl); i++)
74 {
75 memset(&item, 0, sizeof(LV_ITEM));
76 item.mask = LVIF_PARAM;
77 item.iItem = i;
78 (void)ListView_GetItem(hProcessPageListCtrl, &item);
79 pData = (LPPROCESS_PAGE_LIST_ITEM)item.lParam;
80 if (pData->ProcessId == dwProcessId)
81 {
82 return i;
83 }
84 }
85 return 0;
86 }
87
88 DWORD GetSelectedProcessId(void)
89 {
90 int Index;
91 LVITEM lvitem;
92
93 if(ListView_GetSelectedCount(hProcessPageListCtrl) == 1)
94 {
95 Index = ListView_GetSelectionMark(hProcessPageListCtrl);
96
97 memset(&lvitem, 0, sizeof(LVITEM));
98
99 lvitem.mask = LVIF_PARAM;
100 lvitem.iItem = Index;
101
102 (void)ListView_GetItem(hProcessPageListCtrl, &lvitem);
103
104 if (lvitem.lParam)
105 return ((LPPROCESS_PAGE_LIST_ITEM)lvitem.lParam)->ProcessId;
106 }
107
108 return 0;
109 }
110
111 void ProcessPageUpdate(void)
112 {
113 /* Enable or disable the "End Process" button */
114 if (ListView_GetSelectedCount(hProcessPageListCtrl))
115 EnableWindow(hProcessPageEndProcessButton, TRUE);
116 else
117 EnableWindow(hProcessPageEndProcessButton, FALSE);
118 }
119
120 INT_PTR CALLBACK
121 ProcessPageWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
122 {
123 RECT rc;
124 int nXDifference;
125 int nYDifference;
126 int cx, cy;
127
128 switch (message) {
129 case WM_INITDIALOG:
130 /*
131 * Save the width and height
132 */
133 GetClientRect(hDlg, &rc);
134 nProcessPageWidth = rc.right;
135 nProcessPageHeight = rc.bottom;
136
137 /* Update window position */
138 SetWindowPos(hDlg, NULL, 15, 30, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
139
140 /*
141 * Get handles to the controls
142 */
143 hProcessPageListCtrl = GetDlgItem(hDlg, IDC_PROCESSLIST);
144 hProcessPageHeaderCtrl = ListView_GetHeader(hProcessPageListCtrl);
145 hProcessPageEndProcessButton = GetDlgItem(hDlg, IDC_ENDPROCESS);
146 hProcessPageShowAllProcessesButton = GetDlgItem(hDlg, IDC_SHOWALLPROCESSES);
147
148 /*
149 * Set the title, and extended window styles for the list control
150 */
151 SetWindowTextW(hProcessPageListCtrl, L"Processes");
152 (void)ListView_SetExtendedListViewStyle(hProcessPageListCtrl, ListView_GetExtendedListViewStyle(hProcessPageListCtrl) | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);
153
154 AddColumns();
155
156 /*
157 * Subclass the process list control so we can intercept WM_ERASEBKGND
158 */
159 OldProcessListWndProc = (WNDPROC)SetWindowLongPtrW(hProcessPageListCtrl, GWLP_WNDPROC, (LONG_PTR)ProcessListWndProc);
160
161 #ifdef RUN_PROC_PAGE
162 /* Start our refresh thread */
163 hProcessThread = CreateThread(NULL, 0, ProcessPageRefreshThread, NULL, 0, &dwProcessThread);
164 #endif
165
166 /* Refresh page */
167 ProcessPageUpdate();
168
169 return TRUE;
170
171 case WM_DESTROY:
172 /* Close the event handle, this will make the */
173 /* refresh thread exit when the wait fails */
174 #ifdef RUN_PROC_PAGE
175 EndLocalThread(&hProcessThread, dwProcessThread);
176 #endif
177 SaveColumnSettings();
178 Cleanup();
179 break;
180
181 case WM_COMMAND:
182 /* Handle the button clicks */
183 switch (LOWORD(wParam))
184 {
185 case IDC_ENDPROCESS:
186 ProcessPage_OnEndProcess();
187 }
188 break;
189
190 case WM_SIZE:
191 if (wParam == SIZE_MINIMIZED)
192 return 0;
193
194 cx = LOWORD(lParam);
195 cy = HIWORD(lParam);
196 nXDifference = cx - nProcessPageWidth;
197 nYDifference = cy - nProcessPageHeight;
198 nProcessPageWidth = cx;
199 nProcessPageHeight = cy;
200
201 /* Reposition the application page's controls */
202 GetWindowRect(hProcessPageListCtrl, &rc);
203 cx = (rc.right - rc.left) + nXDifference;
204 cy = (rc.bottom - rc.top) + nYDifference;
205 SetWindowPos(hProcessPageListCtrl, NULL, 0, 0, cx, cy, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOMOVE|SWP_NOZORDER);
206 InvalidateRect(hProcessPageListCtrl, NULL, TRUE);
207
208 GetClientRect(hProcessPageEndProcessButton, &rc);
209 MapWindowPoints(hProcessPageEndProcessButton, hDlg, (LPPOINT)(PRECT)(&rc), sizeof(RECT)/sizeof(POINT));
210 cx = rc.left + nXDifference;
211 cy = rc.top + nYDifference;
212 SetWindowPos(hProcessPageEndProcessButton, NULL, cx, cy, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
213 InvalidateRect(hProcessPageEndProcessButton, NULL, TRUE);
214
215 GetClientRect(hProcessPageShowAllProcessesButton, &rc);
216 MapWindowPoints(hProcessPageShowAllProcessesButton, hDlg, (LPPOINT)(PRECT)(&rc), sizeof(RECT)/sizeof(POINT));
217 cx = rc.left;
218 cy = rc.top + nYDifference;
219 SetWindowPos(hProcessPageShowAllProcessesButton, NULL, cx, cy, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
220 InvalidateRect(hProcessPageShowAllProcessesButton, NULL, TRUE);
221 break;
222
223 case WM_NOTIFY:
224 ProcessPageOnNotify(wParam, lParam);
225 break;
226
227 case WM_KEYDOWN:
228 if (wParam == VK_DELETE)
229 ProcessPage_OnEndProcess();
230 break;
231 }
232
233 return 0;
234 }
235
236 void ProcessPageOnNotify(WPARAM wParam, LPARAM lParam)
237 {
238 LPNMHDR pnmh;
239 NMLVDISPINFO* pnmdi;
240 LPNMHEADER pnmhdr;
241 ULONG Index;
242 ULONG ColumnIndex;
243 LPPROCESS_PAGE_LIST_ITEM pData;
244
245 pnmh = (LPNMHDR) lParam;
246 pnmdi = (NMLVDISPINFO*) lParam;
247 pnmhdr = (LPNMHEADER) lParam;
248
249 if (pnmh->hwndFrom == hProcessPageListCtrl)
250 {
251 switch (pnmh->code)
252 {
253 case LVN_ITEMCHANGED:
254 ProcessPageUpdate();
255 break;
256
257 case LVN_GETDISPINFO:
258
259 if (!(pnmdi->item.mask & LVIF_TEXT))
260 break;
261
262 pData = (LPPROCESS_PAGE_LIST_ITEM)pnmdi->item.lParam;
263 Index = PerfDataGetProcessIndex(pData->ProcessId);
264 ColumnIndex = pnmdi->item.iSubItem;
265
266 PerfDataGetText(Index, ColumnIndex, pnmdi->item.pszText, (ULONG)pnmdi->item.cchTextMax);
267
268 break;
269
270 case NM_RCLICK:
271
272 ProcessPageShowContextMenu(GetSelectedProcessId());
273 break;
274
275 case LVN_KEYDOWN:
276
277 if (((LPNMLVKEYDOWN)lParam)->wVKey == VK_DELETE)
278 ProcessPage_OnEndProcess();
279 break;
280
281 }
282 }
283 else if (pnmh->hwndFrom == hProcessPageHeaderCtrl)
284 {
285 switch (pnmh->code)
286 {
287 case HDN_ITEMCLICK:
288
289 TaskManagerSettings.SortColumn = ColumnDataHints[pnmhdr->iItem];
290 TaskManagerSettings.SortAscending = !TaskManagerSettings.SortAscending;
291 (void)ListView_SortItems(hProcessPageListCtrl, ProcessPageCompareFunc, NULL);
292
293 break;
294
295 case HDN_ITEMCHANGED:
296
297 UpdateColumnDataHints();
298
299 break;
300
301 case HDN_ENDDRAG:
302
303 UpdateColumnDataHints();
304
305 break;
306
307 }
308 }
309 }
310
311 /*
312 * Adapted from SH_FormatInteger in dll/win32/shell32/dialogs/filedefext.cpp.
313 */
314 UINT
315 SH_FormatInteger(
316 _In_ LONGLONG Num,
317 _Out_writes_z_(cchResultMax) LPWSTR pwszResult,
318 _In_ UINT cchResultMax)
319 {
320 NUMBERFMTW nf;
321 INT i;
322 INT cchGrouping, cchResult;
323 WCHAR wszNumber[24];
324 WCHAR wszDecimalSep[8], wszThousandSep[8];
325 WCHAR wszGrouping[12];
326
327 /* Print the number in uniform mode */
328 StringCchPrintfW(wszNumber, _countof(wszNumber), L"%I64u", Num);
329
330 /* Get system strings for decimal and thousand separators */
331 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
332 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
333
334 /* Initialize format for printing the number in bytes */
335 ZeroMemory(&nf, sizeof(nf));
336 nf.lpDecimalSep = wszDecimalSep;
337 nf.lpThousandSep = wszThousandSep;
338
339 /* Get system string for groups separator */
340 cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
341 LOCALE_SGROUPING,
342 wszGrouping,
343 _countof(wszGrouping));
344
345 /* Convert grouping specs from string to integer */
346 for (i = 0; i < cchGrouping; i++)
347 {
348 WCHAR wch = wszGrouping[i];
349
350 if (wch >= L'0' && wch <= L'9')
351 nf.Grouping = nf.Grouping * 10 + (wch - L'0');
352 else if (wch != L';')
353 break;
354 }
355
356 if ((nf.Grouping % 10) == 0)
357 nf.Grouping /= 10;
358 else
359 nf.Grouping *= 10;
360
361 /* Format the number */
362 cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
363 0,
364 wszNumber,
365 &nf,
366 pwszResult,
367 cchResultMax);
368 if (!cchResult)
369 return 0;
370
371 /* GetNumberFormatW returns number of characters including UNICODE_NULL */
372 return cchResult - 1;
373 }
374
375 void ProcessPageShowContextMenu(DWORD dwProcessId)
376 {
377 HMENU hMenu;
378 HMENU hSubMenu;
379 HMENU hPriorityMenu;
380 POINT pt;
381 SYSTEM_INFO si;
382 HANDLE hProcess;
383 DWORD dwProcessPriorityClass;
384 WCHAR strDebugger[260];
385 DWORD dwDebuggerSize;
386 HKEY hKey;
387
388 if (dwProcessId == 0)
389 return;
390
391 memset(&si, 0, sizeof(SYSTEM_INFO));
392
393 GetCursorPos(&pt);
394 GetSystemInfo(&si);
395
396 hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(IDR_PROCESS_PAGE_CONTEXT));
397 hSubMenu = GetSubMenu(hMenu, 0);
398 hPriorityMenu = GetSubMenu(hSubMenu, 4);
399
400 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
401 dwProcessPriorityClass = GetPriorityClass(hProcess);
402 CloseHandle(hProcess);
403
404 if (si.dwNumberOfProcessors < 2)
405 RemoveMenu(hSubMenu, ID_PROCESS_PAGE_SETAFFINITY, MF_BYCOMMAND);
406
407 switch (dwProcessPriorityClass) {
408 case REALTIME_PRIORITY_CLASS:
409 CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, MF_BYCOMMAND);
410 break;
411 case HIGH_PRIORITY_CLASS:
412 CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_HIGH, MF_BYCOMMAND);
413 break;
414 case ABOVE_NORMAL_PRIORITY_CLASS:
415 CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_ABOVENORMAL, MF_BYCOMMAND);
416 break;
417 case NORMAL_PRIORITY_CLASS:
418 CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_NORMAL, MF_BYCOMMAND);
419 break;
420 case BELOW_NORMAL_PRIORITY_CLASS:
421 CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_BELOWNORMAL, MF_BYCOMMAND);
422 break;
423 case IDLE_PRIORITY_CLASS:
424 CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_LOW, MF_BYCOMMAND);
425 break;
426 }
427
428 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
429 {
430 dwDebuggerSize = sizeof(strDebugger);
431 if (RegQueryValueExW(hKey, L"Debugger", NULL, NULL, (LPBYTE)strDebugger, &dwDebuggerSize) == ERROR_SUCCESS)
432 {
433 CharUpper(strDebugger);
434 if (wcsstr(strDebugger, L"DRWTSN32"))
435 EnableMenuItem(hSubMenu, ID_PROCESS_PAGE_DEBUG, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
436 }
437 else
438 EnableMenuItem(hSubMenu, ID_PROCESS_PAGE_DEBUG, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
439
440 RegCloseKey(hKey);
441 } else {
442 EnableMenuItem(hSubMenu, ID_PROCESS_PAGE_DEBUG, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
443 }
444 TrackPopupMenu(hSubMenu, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_LEFTBUTTON, pt.x, pt.y, 0, hMainWnd, NULL);
445 DestroyMenu(hMenu);
446 }
447
448 void RefreshProcessPage(void)
449 {
450 #ifdef RUN_PROC_PAGE
451 /* Signal the event so that our refresh thread */
452 /* will wake up and refresh the process page */
453 PostThreadMessage(dwProcessThread, WM_TIMER, 0, 0);
454 #endif
455 }
456
457 DWORD WINAPI ProcessPageRefreshThread(void *lpParameter)
458 {
459 MSG msg;
460
461 while (1) {
462 /* Wait for an the event or application close */
463 if (GetMessage(&msg, NULL, 0, 0) <= 0)
464 return 0;
465
466 if (msg.message == WM_TIMER) {
467
468 UpdateProcesses();
469
470 if (IsWindowVisible(hProcessPage))
471 InvalidateRect(hProcessPageListCtrl, NULL, FALSE);
472
473 ProcessPageUpdate();
474 }
475 }
476 return 0;
477 }
478
479 void UpdateProcesses()
480 {
481 int i;
482 ULONG l;
483 LV_ITEM item;
484 LPPROCESS_PAGE_LIST_ITEM pData;
485
486 SendMessage(hProcessPageListCtrl, WM_SETREDRAW, FALSE, 0);
487
488 /* Remove old processes */
489 for (i = 0; i < ListView_GetItemCount(hProcessPageListCtrl); i++)
490 {
491 memset(&item, 0, sizeof (LV_ITEM));
492 item.mask = LVIF_PARAM;
493 item.iItem = i;
494 (void)ListView_GetItem(hProcessPageListCtrl, &item);
495 pData = (LPPROCESS_PAGE_LIST_ITEM)item.lParam;
496 if (!ProcessRunning(pData->ProcessId))
497 {
498 (void)ListView_DeleteItem(hProcessPageListCtrl, i);
499 HeapFree(GetProcessHeap(), 0, pData);
500 }
501 }
502
503 /* Check for difference in listview process and performance process counts */
504 if (ListView_GetItemCount(hProcessPageListCtrl) != PerfDataGetProcessCount())
505 {
506 /* Add new processes by checking against the current items */
507 for (l = 0; l < PerfDataGetProcessCount(); l++)
508 {
509 AddProcess(l);
510 }
511 }
512
513 if (TaskManagerSettings.SortColumn != -1)
514 {
515 (void)ListView_SortItems(hProcessPageListCtrl, ProcessPageCompareFunc, NULL);
516 }
517
518 SendMessage(hProcessPageListCtrl, WM_SETREDRAW, TRUE, 0);
519
520 /* Select first item if any */
521 if ((ListView_GetNextItem(hProcessPageListCtrl, -1, LVNI_FOCUSED | LVNI_SELECTED) == -1) &&
522 (ListView_GetItemCount(hProcessPageListCtrl) > 0) && !bProcessPageSelectionMade)
523 {
524 ListView_SetItemState(hProcessPageListCtrl, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
525 bProcessPageSelectionMade = TRUE;
526 }
527 /*
528 else
529 {
530 bProcessPageSelectionMade = FALSE;
531 }
532 */
533 }
534
535 BOOL ProcessRunning(ULONG ProcessId)
536 {
537 HANDLE hProcess;
538 DWORD exitCode;
539
540 if (ProcessId == 0) {
541 return TRUE;
542 }
543
544 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
545 if (hProcess == NULL) {
546 return FALSE;
547 }
548
549 if (GetExitCodeProcess(hProcess, &exitCode)) {
550 CloseHandle(hProcess);
551 return (exitCode == STILL_ACTIVE);
552 }
553
554 CloseHandle(hProcess);
555 return FALSE;
556 }
557
558 void AddProcess(ULONG Index)
559 {
560 LPPROCESS_PAGE_LIST_ITEM pData;
561 int i;
562 LV_ITEM item;
563 BOOL bAlreadyInList = FALSE;
564 ULONG pid;
565
566 pid = PerfDataGetProcessId(Index);
567
568 /* Check to see if it's already in our list */
569 for (i=0; i<ListView_GetItemCount(hProcessPageListCtrl); i++)
570 {
571 memset(&item, 0, sizeof(LV_ITEM));
572 item.mask = LVIF_PARAM;
573 item.iItem = i;
574 (void)ListView_GetItem(hProcessPageListCtrl, &item);
575 pData = (LPPROCESS_PAGE_LIST_ITEM)item.lParam;
576 if (pData->ProcessId == pid)
577 {
578 bAlreadyInList = TRUE;
579 break;
580 }
581 }
582 if (!bAlreadyInList) /* Add */
583 {
584 pData = (LPPROCESS_PAGE_LIST_ITEM)HeapAlloc(GetProcessHeap(), 0, sizeof(PROCESS_PAGE_LIST_ITEM));
585 pData->ProcessId = pid;
586
587 /* Add the item to the list */
588 memset(&item, 0, sizeof(LV_ITEM));
589 item.mask = LVIF_TEXT|LVIF_PARAM;
590 item.pszText = LPSTR_TEXTCALLBACK;
591 item.iItem = ListView_GetItemCount(hProcessPageListCtrl);
592 item.lParam = (LPARAM)pData;
593 (void)ListView_InsertItem(hProcessPageListCtrl, &item);
594 }
595 }
596
597 BOOL PerfDataGetText(ULONG Index, ULONG ColumnIndex, LPTSTR lpText, ULONG nMaxCount)
598 {
599 IO_COUNTERS iocounters;
600
601 switch (ColumnDataHints[ColumnIndex])
602 {
603 case COLUMN_IMAGENAME:
604 PerfDataGetImageName(Index, lpText, nMaxCount);
605 return TRUE;
606
607 case COLUMN_PID:
608 StringCchPrintfW(lpText, nMaxCount, L"%lu", PerfDataGetProcessId(Index));
609 return TRUE;
610
611 case COLUMN_USERNAME:
612 PerfDataGetUserName(Index, lpText, nMaxCount);
613 return TRUE;
614
615 case COLUMN_COMMANDLINE:
616 PerfDataGetCommandLine(Index, lpText, nMaxCount);
617 return TRUE;
618
619 case COLUMN_SESSIONID:
620 StringCchPrintfW(lpText, nMaxCount, L"%lu", PerfDataGetSessionId(Index));
621 return TRUE;
622
623 case COLUMN_CPUUSAGE:
624 StringCchPrintfW(lpText, nMaxCount, L"%02lu", PerfDataGetCPUUsage(Index));
625 return TRUE;
626
627 case COLUMN_CPUTIME:
628 {
629 LARGE_INTEGER time;
630 DWORD dwHours;
631 DWORD dwMinutes;
632 DWORD dwSeconds;
633
634 time = PerfDataGetCPUTime(Index);
635 gethmsfromlargeint(time, &dwHours, &dwMinutes, &dwSeconds);
636 StringCchPrintfW(lpText, nMaxCount, L"%lu:%02lu:%02lu", dwHours, dwMinutes, dwSeconds);
637 return TRUE;
638 }
639
640 case COLUMN_MEMORYUSAGE:
641 SH_FormatInteger(PerfDataGetWorkingSetSizeBytes(Index) / 1024, lpText, nMaxCount);
642 StringCchCatW(lpText, nMaxCount, L" K");
643 return TRUE;
644
645 case COLUMN_PEAKMEMORYUSAGE:
646 SH_FormatInteger(PerfDataGetPeakWorkingSetSizeBytes(Index) / 1024, lpText, nMaxCount);
647 StringCchCatW(lpText, nMaxCount, L" K");
648 return TRUE;
649
650 case COLUMN_MEMORYUSAGEDELTA:
651 SH_FormatInteger(PerfDataGetWorkingSetSizeDelta(Index) / 1024, lpText, nMaxCount);
652 StringCchCatW(lpText, nMaxCount, L" K");
653 return TRUE;
654
655 case COLUMN_PAGEFAULTS:
656 SH_FormatInteger(PerfDataGetPageFaultCount(Index), lpText, nMaxCount);
657 return TRUE;
658
659 case COLUMN_PAGEFAULTSDELTA:
660 SH_FormatInteger(PerfDataGetPageFaultCountDelta(Index), lpText, nMaxCount);
661 return TRUE;
662
663 case COLUMN_VIRTUALMEMORYSIZE:
664 SH_FormatInteger(PerfDataGetVirtualMemorySizeBytes(Index) / 1024, lpText, nMaxCount);
665 StringCchCatW(lpText, nMaxCount, L" K");
666 return TRUE;
667
668 case COLUMN_PAGEDPOOL:
669 SH_FormatInteger(PerfDataGetPagedPoolUsagePages(Index) / 1024, lpText, nMaxCount);
670 StringCchCatW(lpText, nMaxCount, L" K");
671 return TRUE;
672
673 case COLUMN_NONPAGEDPOOL:
674 SH_FormatInteger(PerfDataGetNonPagedPoolUsagePages(Index) / 1024, lpText, nMaxCount);
675 StringCchCatW(lpText, nMaxCount, L" K");
676 return TRUE;
677
678 case COLUMN_BASEPRIORITY:
679 StringCchPrintfW(lpText, nMaxCount, L"%lu", PerfDataGetBasePriority(Index));
680 return TRUE;
681
682 case COLUMN_HANDLECOUNT:
683 SH_FormatInteger(PerfDataGetHandleCount(Index), lpText, nMaxCount);
684 return TRUE;
685
686 case COLUMN_THREADCOUNT:
687 SH_FormatInteger(PerfDataGetThreadCount(Index), lpText, nMaxCount);
688 return TRUE;
689
690 case COLUMN_USEROBJECTS:
691 SH_FormatInteger(PerfDataGetUSERObjectCount(Index), lpText, nMaxCount);
692 return TRUE;
693
694 case COLUMN_GDIOBJECTS:
695 SH_FormatInteger(PerfDataGetGDIObjectCount(Index), lpText, nMaxCount);
696 return TRUE;
697
698 case COLUMN_IOREADS:
699 PerfDataGetIOCounters(Index, &iocounters);
700 SH_FormatInteger(iocounters.ReadOperationCount, lpText, nMaxCount);
701 return TRUE;
702
703 case COLUMN_IOWRITES:
704 PerfDataGetIOCounters(Index, &iocounters);
705 SH_FormatInteger(iocounters.WriteOperationCount, lpText, nMaxCount);
706 return TRUE;
707
708 case COLUMN_IOOTHER:
709 PerfDataGetIOCounters(Index, &iocounters);
710 SH_FormatInteger(iocounters.OtherOperationCount, lpText, nMaxCount);
711 return TRUE;
712
713 case COLUMN_IOREADBYTES:
714 PerfDataGetIOCounters(Index, &iocounters);
715 SH_FormatInteger(iocounters.ReadTransferCount, lpText, nMaxCount);
716 return TRUE;
717
718 case COLUMN_IOWRITEBYTES:
719 PerfDataGetIOCounters(Index, &iocounters);
720 SH_FormatInteger(iocounters.WriteTransferCount, lpText, nMaxCount);
721 return TRUE;
722
723 case COLUMN_IOOTHERBYTES:
724 PerfDataGetIOCounters(Index, &iocounters);
725 SH_FormatInteger(iocounters.OtherTransferCount, lpText, nMaxCount);
726 return TRUE;
727 }
728
729 return FALSE;
730 }
731
732
733 void gethmsfromlargeint(LARGE_INTEGER largeint, DWORD *dwHours, DWORD *dwMinutes, DWORD *dwSeconds)
734 {
735 #ifdef _MSC_VER
736 *dwHours = (DWORD)(largeint.QuadPart / 36000000000L);
737 *dwMinutes = (DWORD)((largeint.QuadPart % 36000000000L) / 600000000L);
738 *dwSeconds = (DWORD)(((largeint.QuadPart % 36000000000L) % 600000000L) / 10000000L);
739 #else
740 *dwHours = (DWORD)(largeint.QuadPart / 36000000000LL);
741 *dwMinutes = (DWORD)((largeint.QuadPart % 36000000000LL) / 600000000LL);
742 *dwSeconds = (DWORD)(((largeint.QuadPart % 36000000000LL) % 600000000LL) / 10000000LL);
743 #endif
744 }
745
746 int largeintcmp(LARGE_INTEGER l1, LARGE_INTEGER l2)
747 {
748 int ret = 0;
749 DWORD dwHours1;
750 DWORD dwMinutes1;
751 DWORD dwSeconds1;
752 DWORD dwHours2;
753 DWORD dwMinutes2;
754 DWORD dwSeconds2;
755
756 gethmsfromlargeint(l1, &dwHours1, &dwMinutes1, &dwSeconds1);
757 gethmsfromlargeint(l2, &dwHours2, &dwMinutes2, &dwSeconds2);
758 ret = CMP(dwHours1, dwHours2);
759 if (ret == 0)
760 {
761 ret = CMP(dwMinutes1, dwMinutes2);
762 if (ret == 0)
763 {
764 ret = CMP(dwSeconds1, dwSeconds2);
765 }
766 }
767 return ret;
768 }
769
770 int CALLBACK ProcessPageCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
771 {
772 int ret = 0;
773 LPPROCESS_PAGE_LIST_ITEM Param1;
774 LPPROCESS_PAGE_LIST_ITEM Param2;
775 ULONG IndexParam1;
776 ULONG IndexParam2;
777 WCHAR text1[260];
778 WCHAR text2[260];
779 ULONG l1;
780 ULONG l2;
781 LARGE_INTEGER time1;
782 LARGE_INTEGER time2;
783 IO_COUNTERS iocounters1;
784 IO_COUNTERS iocounters2;
785 ULONGLONG ull1;
786 ULONGLONG ull2;
787
788 if (TaskManagerSettings.SortAscending) {
789 Param1 = (LPPROCESS_PAGE_LIST_ITEM)lParam1;
790 Param2 = (LPPROCESS_PAGE_LIST_ITEM)lParam2;
791 } else {
792 Param1 = (LPPROCESS_PAGE_LIST_ITEM)lParam2;
793 Param2 = (LPPROCESS_PAGE_LIST_ITEM)lParam1;
794 }
795 IndexParam1 = PerfDataGetProcessIndex(Param1->ProcessId);
796 IndexParam2 = PerfDataGetProcessIndex(Param2->ProcessId);
797
798 if (TaskManagerSettings.SortColumn == COLUMN_IMAGENAME)
799 {
800 PerfDataGetImageName(IndexParam1, text1, _countof(text1));
801 PerfDataGetImageName(IndexParam2, text2, _countof(text2));
802 ret = _wcsicmp(text1, text2);
803 }
804 else if (TaskManagerSettings.SortColumn == COLUMN_PID)
805 {
806 l1 = Param1->ProcessId;
807 l2 = Param2->ProcessId;
808 ret = CMP(l1, l2);
809 }
810 else if (TaskManagerSettings.SortColumn == COLUMN_USERNAME)
811 {
812 PerfDataGetUserName(IndexParam1, text1, _countof(text1));
813 PerfDataGetUserName(IndexParam2, text2, _countof(text2));
814 ret = _wcsicmp(text1, text2);
815 }
816 else if (TaskManagerSettings.SortColumn == COLUMN_COMMANDLINE)
817 {
818 PerfDataGetCommandLine(IndexParam1, text1, _countof(text1));
819 PerfDataGetCommandLine(IndexParam2, text2, _countof(text2));
820 ret = _wcsicmp(text1, text2);
821 }
822 else if (TaskManagerSettings.SortColumn == COLUMN_SESSIONID)
823 {
824 l1 = PerfDataGetSessionId(IndexParam1);
825 l2 = PerfDataGetSessionId(IndexParam2);
826 ret = CMP(l1, l2);
827 }
828 else if (TaskManagerSettings.SortColumn == COLUMN_CPUUSAGE)
829 {
830 l1 = PerfDataGetCPUUsage(IndexParam1);
831 l2 = PerfDataGetCPUUsage(IndexParam2);
832 ret = CMP(l1, l2);
833 }
834 else if (TaskManagerSettings.SortColumn == COLUMN_CPUTIME)
835 {
836 time1 = PerfDataGetCPUTime(IndexParam1);
837 time2 = PerfDataGetCPUTime(IndexParam2);
838 ret = largeintcmp(time1, time2);
839 }
840 else if (TaskManagerSettings.SortColumn == COLUMN_MEMORYUSAGE)
841 {
842 l1 = PerfDataGetWorkingSetSizeBytes(IndexParam1);
843 l2 = PerfDataGetWorkingSetSizeBytes(IndexParam2);
844 ret = CMP(l1, l2);
845 }
846 else if (TaskManagerSettings.SortColumn == COLUMN_PEAKMEMORYUSAGE)
847 {
848 l1 = PerfDataGetPeakWorkingSetSizeBytes(IndexParam1);
849 l2 = PerfDataGetPeakWorkingSetSizeBytes(IndexParam2);
850 ret = CMP(l1, l2);
851 }
852 else if (TaskManagerSettings.SortColumn == COLUMN_MEMORYUSAGEDELTA)
853 {
854 l1 = PerfDataGetWorkingSetSizeDelta(IndexParam1);
855 l2 = PerfDataGetWorkingSetSizeDelta(IndexParam2);
856 ret = CMP(l1, l2);
857 }
858 else if (TaskManagerSettings.SortColumn == COLUMN_PAGEFAULTS)
859 {
860 l1 = PerfDataGetPageFaultCount(IndexParam1);
861 l2 = PerfDataGetPageFaultCount(IndexParam2);
862 ret = CMP(l1, l2);
863 }
864 else if (TaskManagerSettings.SortColumn == COLUMN_PAGEFAULTSDELTA)
865 {
866 l1 = PerfDataGetPageFaultCountDelta(IndexParam1);
867 l2 = PerfDataGetPageFaultCountDelta(IndexParam2);
868 ret = CMP(l1, l2);
869 }
870 else if (TaskManagerSettings.SortColumn == COLUMN_VIRTUALMEMORYSIZE)
871 {
872 l1 = PerfDataGetVirtualMemorySizeBytes(IndexParam1);
873 l2 = PerfDataGetVirtualMemorySizeBytes(IndexParam2);
874 ret = CMP(l1, l2);
875 }
876 else if (TaskManagerSettings.SortColumn == COLUMN_PAGEDPOOL)
877 {
878 l1 = PerfDataGetPagedPoolUsagePages(IndexParam1);
879 l2 = PerfDataGetPagedPoolUsagePages(IndexParam2);
880 ret = CMP(l1, l2);
881 }
882 else if (TaskManagerSettings.SortColumn == COLUMN_NONPAGEDPOOL)
883 {
884 l1 = PerfDataGetNonPagedPoolUsagePages(IndexParam1);
885 l2 = PerfDataGetNonPagedPoolUsagePages(IndexParam2);
886 ret = CMP(l1, l2);
887 }
888 else if (TaskManagerSettings.SortColumn == COLUMN_BASEPRIORITY)
889 {
890 l1 = PerfDataGetBasePriority(IndexParam1);
891 l2 = PerfDataGetBasePriority(IndexParam2);
892 ret = CMP(l1, l2);
893 }
894 else if (TaskManagerSettings.SortColumn == COLUMN_HANDLECOUNT)
895 {
896 l1 = PerfDataGetHandleCount(IndexParam1);
897 l2 = PerfDataGetHandleCount(IndexParam2);
898 ret = CMP(l1, l2);
899 }
900 else if (TaskManagerSettings.SortColumn == COLUMN_THREADCOUNT)
901 {
902 l1 = PerfDataGetThreadCount(IndexParam1);
903 l2 = PerfDataGetThreadCount(IndexParam2);
904 ret = CMP(l1, l2);
905 }
906 else if (TaskManagerSettings.SortColumn == COLUMN_USEROBJECTS)
907 {
908 l1 = PerfDataGetUSERObjectCount(IndexParam1);
909 l2 = PerfDataGetUSERObjectCount(IndexParam2);
910 ret = CMP(l1, l2);
911 }
912 else if (TaskManagerSettings.SortColumn == COLUMN_GDIOBJECTS)
913 {
914 l1 = PerfDataGetGDIObjectCount(IndexParam1);
915 l2 = PerfDataGetGDIObjectCount(IndexParam2);
916 ret = CMP(l1, l2);
917 }
918 else if (TaskManagerSettings.SortColumn == COLUMN_IOREADS)
919 {
920 PerfDataGetIOCounters(IndexParam1, &iocounters1);
921 PerfDataGetIOCounters(IndexParam2, &iocounters2);
922 ull1 = iocounters1.ReadOperationCount;
923 ull2 = iocounters2.ReadOperationCount;
924 ret = CMP(ull1, ull2);
925 }
926 else if (TaskManagerSettings.SortColumn == COLUMN_IOWRITES)
927 {
928 PerfDataGetIOCounters(IndexParam1, &iocounters1);
929 PerfDataGetIOCounters(IndexParam2, &iocounters2);
930 ull1 = iocounters1.WriteOperationCount;
931 ull2 = iocounters2.WriteOperationCount;
932 ret = CMP(ull1, ull2);
933 }
934 else if (TaskManagerSettings.SortColumn == COLUMN_IOOTHER)
935 {
936 PerfDataGetIOCounters(IndexParam1, &iocounters1);
937 PerfDataGetIOCounters(IndexParam2, &iocounters2);
938 ull1 = iocounters1.OtherOperationCount;
939 ull2 = iocounters2.OtherOperationCount;
940 ret = CMP(ull1, ull2);
941 }
942 else if (TaskManagerSettings.SortColumn == COLUMN_IOREADBYTES)
943 {
944 PerfDataGetIOCounters(IndexParam1, &iocounters1);
945 PerfDataGetIOCounters(IndexParam2, &iocounters2);
946 ull1 = iocounters1.ReadTransferCount;
947 ull2 = iocounters2.ReadTransferCount;
948 ret = CMP(ull1, ull2);
949 }
950 else if (TaskManagerSettings.SortColumn == COLUMN_IOWRITEBYTES)
951 {
952 PerfDataGetIOCounters(IndexParam1, &iocounters1);
953 PerfDataGetIOCounters(IndexParam2, &iocounters2);
954 ull1 = iocounters1.WriteTransferCount;
955 ull2 = iocounters2.WriteTransferCount;
956 ret = CMP(ull1, ull2);
957 }
958 else if (TaskManagerSettings.SortColumn == COLUMN_IOOTHERBYTES)
959 {
960 PerfDataGetIOCounters(IndexParam1, &iocounters1);
961 PerfDataGetIOCounters(IndexParam2, &iocounters2);
962 ull1 = iocounters1.OtherTransferCount;
963 ull2 = iocounters2.OtherTransferCount;
964 ret = CMP(ull1, ull2);
965 }
966 return ret;
967 }
968
969 /**
970 * @brief
971 * Maps an NT "\Device\..." path to its Win32 "DOS" equivalent.
972 *
973 * @param[in] lpDevicePath
974 * The NT device path to convert.
975 *
976 * @param[out] lpDosPath
977 * Receives the converted Win32 path.
978 *
979 * @param[in] dwLength
980 * Size of the lpDosPath buffer in characters.
981 *
982 * @return
983 * The number of characters required (if lpDosPath == NULL or dwLength == 0),
984 * or actually written in the lpDosPath buffer, including the NULL terminator.
985 * Returns 0 in case of failure.
986 **/
987 static DWORD
988 DevicePathToDosPath(
989 _In_ LPCWSTR lpDevicePath,
990 _Out_writes_to_opt_(dwLength, return)
991 LPWSTR lpDosPath,
992 _In_opt_ DWORD dwLength)
993 {
994 DWORD dwRet = 0;
995 WCHAR szDrive[3] = L"?:";
996 WCHAR szDeviceName[MAX_PATH];
997
998 /* Check if lpDevicePath is a device path */
999 if (_wcsnicmp(lpDevicePath, L"\\Device\\", CONST_STR_LEN(L"\\Device\\")) != 0)
1000 {
1001 return 0;
1002 }
1003
1004 for (szDrive[0] = L'A'; szDrive[0] <= L'`'; szDrive[0]++)
1005 {
1006 if (QueryDosDeviceW(szDrive, szDeviceName, _countof(szDeviceName)) != 0)
1007 {
1008 size_t len = wcslen(szDeviceName);
1009
1010 if (_wcsnicmp(lpDevicePath, szDeviceName, len) == 0)
1011 {
1012 /* Get the required length, including the NULL terminator */
1013 dwRet = _countof(szDrive) + wcslen(lpDevicePath + len);
1014
1015 if (lpDosPath && (dwLength >= dwRet))
1016 {
1017 StringCchPrintfW(lpDosPath, dwLength, L"%s%s",
1018 szDrive, lpDevicePath + len);
1019 }
1020
1021 break;
1022 }
1023 }
1024 }
1025
1026 return dwRet;
1027 }
1028
1029 /**
1030 * @brief
1031 * Retrieves the Win32 path of an executable image, by handle.
1032 *
1033 * @param[in] hProcess
1034 * Handle to the executable image; it should be opened with
1035 * PROCESS_QUERY_INFORMATION access rights.
1036 *
1037 * @param[out] lpExePath
1038 * Receives the Win32 image path.
1039 *
1040 * @param[in] dwLength
1041 * Size of the lpExePath buffer in characters.
1042 *
1043 * @return
1044 * The number of characters required (if lpExePath == NULL or dwLength == 0),
1045 * or actually written in the lpExePath buffer, including the NULL terminator.
1046 * Returns 0 in case of failure.
1047 **/
1048 static DWORD
1049 GetProcessExecutablePath(
1050 _In_ HANDLE hProcess,
1051 _Out_writes_to_opt_(dwLength, return)
1052 LPWSTR lpExePath,
1053 _In_opt_ DWORD dwLength)
1054 {
1055 DWORD dwRet = 0;
1056 NTSTATUS Status;
1057 BYTE StaticBuffer[sizeof(UNICODE_STRING) + (MAX_PATH * sizeof(WCHAR))];
1058 PVOID DynamicBuffer = NULL;
1059 PUNICODE_STRING ExePath;
1060 ULONG SizeNeeded;
1061
1062 Status = NtQueryInformationProcess(hProcess,
1063 ProcessImageFileName,
1064 StaticBuffer,
1065 /* Reserve a NULL terminator */
1066 sizeof(StaticBuffer) - sizeof(WCHAR),
1067 &SizeNeeded);
1068 if (NT_SUCCESS(Status))
1069 {
1070 ExePath = (PUNICODE_STRING)StaticBuffer;
1071 }
1072 else if (Status == STATUS_INFO_LENGTH_MISMATCH)
1073 {
1074 /* Allocate the buffer, reserving space for a NULL terminator */
1075 DynamicBuffer = HeapAlloc(GetProcessHeap(), 0, SizeNeeded + sizeof(WCHAR));
1076 if (!DynamicBuffer)
1077 return 0;
1078
1079 Status = NtQueryInformationProcess(hProcess,
1080 ProcessImageFileName,
1081 DynamicBuffer,
1082 SizeNeeded,
1083 &SizeNeeded);
1084 if (!NT_SUCCESS(Status))
1085 goto Cleanup;
1086
1087 ExePath = DynamicBuffer;
1088 }
1089 else
1090 {
1091 return 0;
1092 }
1093
1094 /* Manually NULL-terminate */
1095 ExePath->Buffer[ExePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
1096
1097 /* HACK: Convert device path format into Win32 path format.
1098 * Use ProcessImageFileNameWin32 instead if the kernel supports it. */
1099 dwRet = DevicePathToDosPath(ExePath->Buffer, lpExePath, dwLength);
1100
1101 Cleanup:
1102 HeapFree(GetProcessHeap(), 0, DynamicBuffer);
1103
1104 return dwRet;
1105 }
1106
1107 /**
1108 * @brief
1109 * Retrieves the Win32 path of an executable image, by identifier.
1110 *
1111 * @param[in] dwProcessId
1112 * Identifier of the running executable image.
1113 *
1114 * @param[out] lpExePath
1115 * Receives the Win32 image path.
1116 *
1117 * @param[in] dwLength
1118 * Size of the lpExePath buffer in characters.
1119 *
1120 * @return
1121 * The number of characters required (if lpExePath == NULL or dwLength == 0),
1122 * or actually written in the lpExePath buffer, including the NULL terminator.
1123 * Returns 0 in case of failure.
1124 **/
1125 static DWORD
1126 GetProcessExecutablePathById(
1127 _In_ DWORD dwProcessId,
1128 _Out_writes_to_opt_(dwLength, return)
1129 LPWSTR lpExePath,
1130 _In_opt_ DWORD dwLength)
1131 {
1132 DWORD dwRet = 0;
1133
1134 if (dwProcessId == 0)
1135 return 0;
1136
1137 /* PID = 4 ("System") */
1138 if (dwProcessId == 4)
1139 {
1140 static const WCHAR szKernelExe[] = L"\\ntoskrnl.exe";
1141 LPWSTR pszSystemDir;
1142 UINT uLength;
1143
1144 uLength = GetSystemDirectoryW(NULL, 0);
1145 if (uLength == 0)
1146 return 0;
1147
1148 pszSystemDir = HeapAlloc(GetProcessHeap(), 0, uLength * sizeof(WCHAR));
1149 if (!pszSystemDir)
1150 return 0;
1151
1152 if (GetSystemDirectoryW(pszSystemDir, uLength) != 0)
1153 {
1154 /* Get the required length, including the NULL terminator */
1155 dwRet = uLength + CONST_STR_LEN(szKernelExe);
1156
1157 if (lpExePath && (dwLength >= dwRet))
1158 {
1159 StringCchPrintfW(lpExePath, dwLength, L"%s%s",
1160 pszSystemDir, szKernelExe);
1161 }
1162 }
1163
1164 HeapFree(GetProcessHeap(), 0, pszSystemDir);
1165 }
1166 else
1167 {
1168 HANDLE hProcess;
1169
1170 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
1171 if (hProcess)
1172 {
1173 dwRet = GetProcessExecutablePath(hProcess, lpExePath, dwLength);
1174 CloseHandle(hProcess);
1175 }
1176 }
1177
1178 return dwRet;
1179 }
1180
1181 void ProcessPage_OnProperties(void)
1182 {
1183 DWORD dwProcessId;
1184 DWORD dwLength;
1185 LPWSTR pszExePath;
1186 SHELLEXECUTEINFOW info = { 0 };
1187
1188 dwProcessId = GetSelectedProcessId();
1189
1190 /* Retrieve the image path length */
1191 dwLength = GetProcessExecutablePathById(dwProcessId, NULL, 0);
1192 if (dwLength == 0)
1193 return;
1194
1195 /* Allocate and retrieve the image path */
1196 pszExePath = HeapAlloc(GetProcessHeap(), 0, dwLength * sizeof(WCHAR));
1197 if (!pszExePath)
1198 return;
1199
1200 if (GetProcessExecutablePathById(dwProcessId, pszExePath, dwLength) == 0)
1201 goto Cleanup;
1202
1203 /* Call the shell to display the file properties */
1204 info.cbSize = sizeof(SHELLEXECUTEINFOW);
1205 info.fMask = SEE_MASK_INVOKEIDLIST;
1206 info.hwnd = NULL;
1207 info.lpVerb = L"properties";
1208 info.lpFile = pszExePath;
1209 info.lpParameters = L"";
1210 info.lpDirectory = NULL;
1211 info.nShow = SW_SHOW;
1212 info.hInstApp = NULL;
1213
1214 ShellExecuteExW(&info);
1215
1216 Cleanup:
1217 HeapFree(GetProcessHeap(), 0, pszExePath);
1218 }
1219
1220 void ProcessPage_OnOpenFileLocation(void)
1221 {
1222 DWORD dwProcessId;
1223 DWORD dwLength;
1224 LPWSTR pszExePath;
1225 LPWSTR pszCmdLine = NULL;
1226
1227 dwProcessId = GetSelectedProcessId();
1228
1229 /* Retrieve the image path length */
1230 dwLength = GetProcessExecutablePathById(dwProcessId, NULL, 0);
1231 if (dwLength == 0)
1232 return;
1233
1234 /* Allocate and retrieve the image path */
1235 pszExePath = HeapAlloc(GetProcessHeap(), 0, dwLength * sizeof(WCHAR));
1236 if (!pszExePath)
1237 return;
1238
1239 if (GetProcessExecutablePathById(dwProcessId, pszExePath, dwLength) == 0)
1240 goto Cleanup;
1241
1242 /* Build the shell command line */
1243 pszCmdLine = HeapAlloc(GetProcessHeap(), 0, (dwLength + CONST_STR_LEN(L"/select,\"\"")) * sizeof(WCHAR));
1244 if (!pszCmdLine)
1245 goto Cleanup;
1246
1247 StringCchPrintfW(pszCmdLine, dwLength + CONST_STR_LEN(L"/select,\"\""), L"/select,\"%s\"", pszExePath);
1248
1249 /* Call the shell to open the file location and select it */
1250 ShellExecuteW(NULL, L"open", L"explorer.exe", pszCmdLine, NULL, SW_SHOWNORMAL);
1251
1252 Cleanup:
1253 HeapFree(GetProcessHeap(), 0, pszCmdLine);
1254 HeapFree(GetProcessHeap(), 0, pszExePath);
1255 }