Process Hacker
miniinfo.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * mini information window
4  *
5  * Copyright (C) 2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phapp.h>
24 #include <settings.h>
25 #include <emenu.h>
26 #include <miniinfo.h>
27 #include <phplug.h>
28 #include <notifico.h>
29 #include <windowsx.h>
30 #include <uxtheme.h>
31 #include <miniinfop.h>
32 
33 static HWND PhMipContainerWindow = NULL;
34 static POINT PhMipSourcePoint;
35 static LONG PhMipPinCounts[MaxMiniInfoPinType];
36 static LONG PhMipMaxPinCounts[] =
37 {
38  1, // MiniInfoManualPinType
39  1, // MiniInfoIconPinType
40  1, // MiniInfoActivePinType
41  1, // MiniInfoHoverPinType
42  1, // MiniInfoChildControlPinType
43 };
44 C_ASSERT(sizeof(PhMipMaxPinCounts) / sizeof(LONG) == MaxMiniInfoPinType);
45 static LONG PhMipDelayedPinAdjustments[MaxMiniInfoPinType];
46 static PPH_MESSAGE_LOOP_FILTER_ENTRY PhMipMessageLoopFilterEntry;
47 static HWND PhMipLastTrackedWindow;
48 static HWND PhMipLastNcTrackedWindow;
49 static ULONG PhMipRefreshAutomatically;
50 static BOOLEAN PhMipPinned;
51 
52 static HWND PhMipWindow = NULL;
53 static PH_LAYOUT_MANAGER PhMipLayoutManager;
54 static RECT MinimumSize;
55 static PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration;
56 static PH_STRINGREF DownArrowPrefix = PH_STRINGREF_INIT(L"\u25be ");
57 static WNDPROC SectionControlOldWndProc;
58 
59 static PPH_LIST SectionList;
60 static PH_MINIINFO_PARAMETERS CurrentParameters;
61 static PPH_MINIINFO_SECTION CurrentSection;
62 
64  _In_ PH_MINIINFO_PIN_TYPE PinType,
65  _In_ LONG PinCount,
66  _In_opt_ ULONG PinDelayMs,
67  _In_ ULONG Flags,
68  _In_opt_ PWSTR SectionName,
69  _In_opt_ PPOINT SourcePoint
70  )
71 {
72  PH_MIP_ADJUST_PIN_RESULT adjustPinResult;
73 
74  if (PinDelayMs && PinCount < 0)
75  {
76  PhMipDelayedPinAdjustments[PinType] = PinCount;
77  SetTimer(PhMipContainerWindow, MIP_TIMER_PIN_FIRST + PinType, PinDelayMs, NULL);
78  return;
79  }
80  else
81  {
82  PhMipDelayedPinAdjustments[PinType] = 0;
83  KillTimer(PhMipContainerWindow, MIP_TIMER_PIN_FIRST + PinType);
84  }
85 
86  adjustPinResult = PhMipAdjustPin(PinType, PinCount);
87 
88  if (adjustPinResult == ShowAdjustPinResult)
89  {
90  PH_RECTANGLE windowRectangle;
91  ULONG opacity;
92 
93  if (SourcePoint)
94  PhMipSourcePoint = *SourcePoint;
95 
96  if (!PhMipContainerWindow)
97  {
98  WNDCLASSEX wcex;
99 
100  memset(&wcex, 0, sizeof(WNDCLASSEX));
101  wcex.cbSize = sizeof(WNDCLASSEX);
102  wcex.style = 0;
103  wcex.lpfnWndProc = PhMipContainerWndProc;
104  wcex.cbClsExtra = 0;
105  wcex.cbWndExtra = 0;
106  wcex.hInstance = PhInstanceHandle;
107  wcex.hIcon = LoadIcon(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER));
108  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
109  wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
110  wcex.lpszClassName = MIP_CONTAINER_CLASSNAME;
111  wcex.hIconSm = (HICON)LoadImage(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER), IMAGE_ICON, 16, 16, 0);
112  RegisterClassEx(&wcex);
113 
114  PhMipContainerWindow = CreateWindow(
116  L"Process Hacker",
117  WS_BORDER | WS_THICKFRAME | WS_POPUP,
118  0,
119  0,
120  400,
121  400,
122  NULL,
123  NULL,
125  NULL
126  );
127  PhSetWindowExStyle(PhMipContainerWindow, WS_EX_TOOLWINDOW, WS_EX_TOOLWINDOW);
128  PhMipWindow = CreateDialog(
130  MAKEINTRESOURCE(IDD_MINIINFO),
131  PhMipContainerWindow,
133  );
134  ShowWindow(PhMipWindow, SW_SHOW);
135 
136  if (PhGetIntegerSetting(L"MiniInfoWindowPinned"))
138 
139  PhMipRefreshAutomatically = PhGetIntegerSetting(L"MiniInfoWindowRefreshAutomatically");
140 
141  opacity = PhGetIntegerSetting(L"MiniInfoWindowOpacity");
142 
143  if (opacity != 0)
144  PhSetWindowOpacity(PhMipContainerWindow, opacity);
145 
146  MinimumSize.left = 0;
147  MinimumSize.top = 0;
148  MinimumSize.right = 210;
149  MinimumSize.bottom = 140;
150  MapDialogRect(PhMipWindow, &MinimumSize);
151  }
152 
153  if (!(Flags & PH_MINIINFO_LOAD_POSITION))
154  {
155  PhMipCalculateWindowRectangle(&PhMipSourcePoint, &windowRectangle);
156  SetWindowPos(
157  PhMipContainerWindow,
158  HWND_TOPMOST,
159  windowRectangle.Left,
160  windowRectangle.Top,
161  windowRectangle.Width,
162  windowRectangle.Height,
163  SWP_NOACTIVATE
164  );
165  }
166  else
167  {
168  PhLoadWindowPlacementFromSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow);
169  SetWindowPos(PhMipContainerWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
170  }
171 
172  ShowWindow(PhMipContainerWindow, (Flags & PH_MINIINFO_ACTIVATE_WINDOW) ? SW_SHOW : SW_SHOWNOACTIVATE);
173  }
174  else if (adjustPinResult == HideAdjustPinResult)
175  {
176  if (PhMipContainerWindow)
177  ShowWindow(PhMipContainerWindow, SW_HIDE);
178  }
179  else
180  {
181  if ((Flags & PH_MINIINFO_ACTIVATE_WINDOW) && IsWindowVisible(PhMipContainerWindow))
182  SetActiveWindow(PhMipContainerWindow);
183  }
184 
185  if (SectionName && (!PhMipPinned || !(Flags & PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED)))
186  {
187  PH_STRINGREF sectionName;
188  PPH_MINIINFO_SECTION section;
189 
190  PhInitializeStringRefLongHint(&sectionName, SectionName);
191 
192  if (section = PhMipFindSection(&sectionName))
193  PhMipChangeSection(section);
194  }
195 }
196 
197 LRESULT CALLBACK PhMipContainerWndProc(
198  _In_ HWND hWnd,
199  _In_ UINT uMsg,
200  _In_ WPARAM wParam,
201  _In_ LPARAM lParam
202  )
203 {
204  switch (uMsg)
205  {
206  case WM_SHOWWINDOW:
207  {
208  PhMipContainerOnShowWindow(!!wParam, (ULONG)lParam);
209  }
210  break;
211  case WM_ACTIVATE:
212  {
213  PhMipContainerOnActivate(LOWORD(wParam), !!HIWORD(wParam));
214  }
215  break;
216  case WM_SIZE:
217  {
219  }
220  break;
221  case WM_SIZING:
222  {
223  PhMipContainerOnSizing((ULONG)wParam, (PRECT)lParam);
224  }
225  break;
226  case WM_EXITSIZEMOVE:
227  {
229  }
230  break;
231  case WM_CLOSE:
232  {
233  // Hide, don't close.
234  ShowWindow(hWnd, SW_HIDE);
235  SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 0);
236  }
237  return TRUE;
238  case WM_ERASEBKGND:
239  {
240  if (PhMipContainerOnEraseBkgnd((HDC)wParam))
241  return TRUE;
242  }
243  break;
244  case WM_TIMER:
245  {
246  PhMipContainerOnTimer((ULONG)wParam);
247  }
248  break;
249  }
250 
251  return DefWindowProc(hWnd, uMsg, wParam, lParam);
252 }
253 
254 INT_PTR CALLBACK PhMipMiniInfoDialogProc(
255  _In_ HWND hwndDlg,
256  _In_ UINT uMsg,
257  _In_ WPARAM wParam,
258  _In_ LPARAM lParam
259  )
260 {
261  switch (uMsg)
262  {
263  case WM_INITDIALOG:
264  {
265  PhMipWindow = hwndDlg;
267  }
268  break;
269  case WM_SHOWWINDOW:
270  {
271  PhMipOnShowWindow(!!wParam, (ULONG)lParam);
272  }
273  break;
274  case WM_COMMAND:
275  {
276  PhMipOnCommand(LOWORD(wParam), HIWORD(wParam));
277  }
278  break;
279  case WM_NOTIFY:
280  {
281  LRESULT result;
282 
283  if (PhMipOnNotify((NMHDR *)lParam, &result))
284  {
285  SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, result);
286  return TRUE;
287  }
288  }
289  break;
290  case WM_CTLCOLORBTN:
291  case WM_CTLCOLORDLG:
292  case WM_CTLCOLORSTATIC:
293  {
294  HBRUSH brush;
295 
296  if (PhMipOnCtlColorXxx(uMsg, (HWND)lParam, (HDC)wParam, &brush))
297  return (INT_PTR)brush;
298  }
299  break;
300  case WM_DRAWITEM:
301  {
302  if (PhMipOnDrawItem(wParam, (DRAWITEMSTRUCT *)lParam))
303  return TRUE;
304  }
305  break;
306  }
307 
308  if (uMsg >= MIP_MSG_FIRST && uMsg <= MIP_MSG_LAST)
309  {
310  PhMipOnUserMessage(uMsg, wParam, lParam);
311  }
312 
313  return FALSE;
314 }
315 
317  _In_ BOOLEAN Showing,
318  _In_ ULONG State
319  )
320 {
321  ULONG i;
322  PPH_MINIINFO_SECTION section;
323 
324  if (Showing)
325  {
326  PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0);
327 
328  PhMipMessageLoopFilterEntry = PhRegisterMessageLoopFilter(PhMipMessageLoopFilter, NULL);
329 
333  NULL,
334  &ProcessesUpdatedRegistration
335  );
336 
338  }
339  else
340  {
341  ULONG i;
342 
343  for (i = 0; i < MaxMiniInfoPinType; i++)
344  PhMipPinCounts[i] = 0;
345 
346  Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PIN), BST_UNCHECKED);
348  PhSetIntegerSetting(L"MiniInfoWindowPinned", FALSE);
349 
352  &ProcessesUpdatedRegistration
353  );
354 
355  if (PhMipMessageLoopFilterEntry)
356  {
357  PhUnregisterMessageLoopFilter(PhMipMessageLoopFilterEntry);
358  PhMipMessageLoopFilterEntry = NULL;
359  }
360 
361  PhSaveWindowPlacementToSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow);
362  }
363 
364  if (SectionList)
365  {
366  for (i = 0; i < SectionList->Count; i++)
367  {
368  section = SectionList->Items[i];
369  section->Callback(section, MiniInfoShowing, (PVOID)Showing, NULL);
370  }
371  }
372 }
373 
375  _In_ ULONG Type,
376  _In_ BOOLEAN Minimized
377  )
378 {
379  if (Type == WA_ACTIVE || Type == WA_CLICKACTIVE)
380  {
381  PhPinMiniInformation(MiniInfoActivePinType, 1, 0, 0, NULL, NULL);
382  }
383  else if (Type == WA_INACTIVE)
384  {
385  PhPinMiniInformation(MiniInfoActivePinType, -1, 0, 0, NULL, NULL);
386  }
387 }
388 
390  VOID
391  )
392 {
393  if (PhMipWindow)
394  {
395  InvalidateRect(PhMipContainerWindow, NULL, FALSE);
396  PhMipLayout();
397  }
398 }
399 
401  _In_ ULONG Edge,
402  _In_ PRECT DragRectangle
403  )
404 {
405  PhResizingMinimumSize(DragRectangle, Edge, MinimumSize.right, MinimumSize.bottom);
406 }
407 
409  VOID
410  )
411 {
412  PhSaveWindowPlacementToSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow);
413 }
414 
416  _In_ HDC hdc
417  )
418 {
419  return FALSE;
420 }
421 
423  _In_ ULONG Id
424  )
425 {
426  if (Id >= MIP_TIMER_PIN_FIRST && Id <= MIP_TIMER_PIN_LAST)
427  {
429 
430  // PhPinMiniInformation kills the timer for us.
431  PhPinMiniInformation(pinType, PhMipDelayedPinAdjustments[pinType], 0, 0, NULL, NULL);
432  }
433 }
434 
436  VOID
437  )
438 {
439  HBITMAP cog;
440  HBITMAP pin;
441 
442  cog = PH_LOAD_SHARED_IMAGE(MAKEINTRESOURCE(IDB_COG), IMAGE_BITMAP);
443  SET_BUTTON_BITMAP(PhMipWindow, IDC_OPTIONS, cog);
444 
445  pin = PH_LOAD_SHARED_IMAGE(MAKEINTRESOURCE(IDB_PIN), IMAGE_BITMAP);
446  SET_BUTTON_BITMAP(PhMipWindow, IDC_PIN, pin);
447 
448  PhInitializeLayoutManager(&PhMipLayoutManager, PhMipWindow);
449  PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_LAYOUT), NULL,
450  PH_ANCHOR_ALL);
451  PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_SECTION), NULL,
453  PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_OPTIONS), NULL,
455  PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_PIN), NULL,
457 
458  SectionControlOldWndProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(PhMipWindow, IDC_SECTION), GWLP_WNDPROC);
459  SetWindowLongPtr(GetDlgItem(PhMipWindow, IDC_SECTION), GWLP_WNDPROC, (LONG_PTR)PhMipSectionControlHookWndProc);
460 
461  Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PIN), !!PhGetIntegerSetting(L"MiniInfoWindowPinned"));
462 }
463 
465  _In_ BOOLEAN Showing,
466  _In_ ULONG State
467  )
468 {
469  if (SectionList)
470  return;
471 
472  SectionList = PhCreateList(8);
474 
475  SendMessage(GetDlgItem(PhMipWindow, IDC_SECTION), WM_SETFONT, (WPARAM)CurrentParameters.MediumFont, FALSE);
476 
481 
482  if (PhPluginsEnabled)
483  {
485 
486  pointers.WindowHandle = PhMipContainerWindow;
488  pointers.FindSection = PhMipFindSection;
491  }
492 
493  PhMipChangeSection(SectionList->Items[0]);
494 }
495 
497  _In_ ULONG Id,
498  _In_ ULONG Code
499  )
500 {
501  switch (Id)
502  {
503  case IDC_SECTION:
504  switch (Code)
505  {
506  case STN_CLICKED:
508  break;
509  }
510  break;
511  case IDC_OPTIONS:
513  break;
514  case IDC_PIN:
515  {
516  BOOLEAN pinned;
517 
518  pinned = Button_GetCheck(GetDlgItem(PhMipWindow, IDC_PIN)) == BST_CHECKED;
519  PhPinMiniInformation(MiniInfoManualPinType, pinned ? 1 : -1, 0, 0, NULL, NULL);
520  PhMipSetPinned(pinned);
521  PhSetIntegerSetting(L"MiniInfoWindowPinned", pinned);
522  }
523  break;
524  }
525 }
526 
528  _In_ NMHDR *Header,
529  _Out_ LRESULT *Result
530  )
531 {
532  return FALSE;
533 }
534 
536  _In_ ULONG Message,
537  _In_ HWND hwnd,
538  _In_ HDC hdc,
539  _Out_ HBRUSH *Brush
540  )
541 {
542  return FALSE;
543 }
544 
546  _In_ ULONG_PTR Id,
547  _In_ DRAWITEMSTRUCT *DrawItemStruct
548  )
549 {
550  return FALSE;
551 }
552 
554  _In_ ULONG Message,
555  _In_ ULONG_PTR WParam,
556  _In_ ULONG_PTR LParam
557  )
558 {
559  switch (Message)
560  {
561  case MIP_MSG_UPDATE:
562  {
563  ULONG i;
564  PPH_MINIINFO_SECTION section;
565 
566  if (SectionList)
567  {
568  for (i = 0; i < SectionList->Count; i++)
569  {
570  section = SectionList->Items[i];
571  section->Callback(section, MiniInfoTick, NULL, NULL);
572  }
573  }
574  }
575  break;
576  }
577 }
578 
580  _In_ PMSG Message,
581  _In_ PVOID Context
582  )
583 {
584  if (Message->hwnd == PhMipContainerWindow || IsChild(PhMipContainerWindow, Message->hwnd))
585  {
586  if (Message->message == WM_MOUSEMOVE || Message->message == WM_NCMOUSEMOVE)
587  {
588  TRACKMOUSEEVENT trackMouseEvent;
589 
590  trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
591  trackMouseEvent.dwFlags = TME_LEAVE | (Message->message == WM_NCMOUSEMOVE ? TME_NONCLIENT : 0);
592  trackMouseEvent.hwndTrack = Message->hwnd;
593  trackMouseEvent.dwHoverTime = 0;
594  TrackMouseEvent(&trackMouseEvent);
595 
596  if (Message->message == WM_MOUSEMOVE)
597  PhMipLastTrackedWindow = Message->hwnd;
598  else
599  PhMipLastNcTrackedWindow = Message->hwnd;
600 
601  PhPinMiniInformation(MiniInfoHoverPinType, 1, 0, 0, NULL, NULL);
602  }
603  else if (Message->message == WM_MOUSELEAVE && Message->hwnd == PhMipLastTrackedWindow)
604  {
606  }
607  else if (Message->message == WM_NCMOUSELEAVE && Message->hwnd == PhMipLastNcTrackedWindow)
608  {
610  }
611  else if (Message->message == WM_KEYDOWN)
612  {
613  switch (Message->wParam)
614  {
615  case VK_F5:
616  PhMipRefresh();
617  break;
618  case VK_F6:
619  case VK_PAUSE:
621  break;
622  }
623  }
624  }
625 
626  return FALSE;
627 }
628 
630  _In_opt_ PVOID Parameter,
631  _In_opt_ PVOID Context
632  )
633 {
634  if (PhMipRefreshAutomatically & MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned))
635  PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0);
636 }
637 
639  _In_ PH_MINIINFO_PIN_TYPE PinType,
640  _In_ LONG PinCount
641  )
642 {
643  LONG oldTotalPinCount;
644  LONG oldPinCount;
645  LONG newPinCount;
646  ULONG i;
647 
648  oldTotalPinCount = 0;
649 
650  for (i = 0; i < MaxMiniInfoPinType; i++)
651  oldTotalPinCount += PhMipPinCounts[i];
652 
653  oldPinCount = PhMipPinCounts[PinType];
654  newPinCount = max(oldPinCount + PinCount, 0);
655  newPinCount = min(newPinCount, PhMipMaxPinCounts[PinType]);
656  PhMipPinCounts[PinType] = newPinCount;
657 
658  if (oldTotalPinCount == 0 && newPinCount > oldPinCount)
659  return ShowAdjustPinResult;
660  else if (oldTotalPinCount > 0 && oldTotalPinCount - oldPinCount + newPinCount == 0)
661  return HideAdjustPinResult;
662  else
663  return NoAdjustPinResult;
664 }
665 
667  _In_ PPOINT SourcePoint,
668  _Out_ PPH_RECTANGLE WindowRectangle
669  )
670 {
671  RECT windowRect;
672  PH_RECTANGLE windowRectangle;
673  PH_RECTANGLE point;
674  MONITORINFO monitorInfo = { sizeof(monitorInfo) };
675 
676  PhLoadWindowPlacementFromSetting(NULL, L"MiniInfoWindowSize", PhMipContainerWindow);
677  GetWindowRect(PhMipContainerWindow, &windowRect);
678  SendMessage(PhMipContainerWindow, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&windowRect); // Adjust for the minimum size.
679  windowRectangle = PhRectToRectangle(windowRect);
680 
681  point.Left = SourcePoint->x;
682  point.Top = SourcePoint->y;
683  point.Width = 0;
684  point.Height = 0;
685  PhCenterRectangle(&windowRectangle, &point);
686 
687  if (GetMonitorInfo(
688  MonitorFromPoint(*SourcePoint, MONITOR_DEFAULTTOPRIMARY),
689  &monitorInfo
690  ))
691  {
692  PH_RECTANGLE bounds;
693 
694  if (memcmp(&monitorInfo.rcWork, &monitorInfo.rcMonitor, sizeof(RECT)) == 0)
695  {
696  HWND trayWindow;
697  RECT taskbarRect;
698 
699  // The taskbar probably has auto-hide enabled. We need to adjust for that.
700  if ((trayWindow = FindWindow(L"Shell_TrayWnd", NULL)) &&
701  GetMonitorInfo(MonitorFromWindow(trayWindow, MONITOR_DEFAULTTOPRIMARY), &monitorInfo) && // Just in case
702  GetWindowRect(trayWindow, &taskbarRect))
703  {
704  LONG monitorMidX = (monitorInfo.rcMonitor.left + monitorInfo.rcMonitor.right) / 2;
705  LONG monitorMidY = (monitorInfo.rcMonitor.top + monitorInfo.rcMonitor.bottom) / 2;
706 
707  if (taskbarRect.right < monitorMidX)
708  {
709  // Left
710  monitorInfo.rcWork.left += taskbarRect.right - taskbarRect.left;
711  }
712  else if (taskbarRect.bottom < monitorMidY)
713  {
714  // Top
715  monitorInfo.rcWork.top += taskbarRect.bottom - taskbarRect.top;
716  }
717  else if (taskbarRect.left > monitorMidX)
718  {
719  // Right
720  monitorInfo.rcWork.right -= taskbarRect.right - taskbarRect.left;
721  }
722  else if (taskbarRect.top > monitorMidY)
723  {
724  // Bottom
725  monitorInfo.rcWork.bottom -= taskbarRect.bottom - taskbarRect.top;
726  }
727  }
728  }
729 
730  bounds = PhRectToRectangle(monitorInfo.rcWork);
731 
732  PhAdjustRectangleToBounds(&windowRectangle, &bounds);
733  }
734 
735  *WindowRectangle = windowRectangle;
736 }
737 
739  VOID
740  )
741 {
742  LOGFONT logFont;
743  HDC hdc;
744  TEXTMETRIC textMetrics;
745  HFONT originalFont;
746 
747  memset(&CurrentParameters, 0, sizeof(PH_MINIINFO_PARAMETERS));
748 
749  CurrentParameters.ContainerWindowHandle = PhMipContainerWindow;
750  CurrentParameters.MiniInfoWindowHandle = PhMipWindow;
751 
752  if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0))
753  {
754  CurrentParameters.Font = CreateFontIndirect(&logFont);
755  }
756  else
757  {
758  CurrentParameters.Font = PhApplicationFont;
759  GetObject(PhApplicationFont, sizeof(LOGFONT), &logFont);
760  }
761 
762  hdc = GetDC(PhMipWindow);
763 
764  logFont.lfHeight -= MulDiv(2, GetDeviceCaps(hdc, LOGPIXELSY), 72);
765  CurrentParameters.MediumFont = CreateFontIndirect(&logFont);
766 
767  originalFont = SelectObject(hdc, CurrentParameters.Font);
768  GetTextMetrics(hdc, &textMetrics);
769  CurrentParameters.FontHeight = textMetrics.tmHeight;
770  CurrentParameters.FontAverageWidth = textMetrics.tmAveCharWidth;
771 
772  SelectObject(hdc, CurrentParameters.MediumFont);
773  GetTextMetrics(hdc, &textMetrics);
774  CurrentParameters.MediumFontHeight = textMetrics.tmHeight;
775  CurrentParameters.MediumFontAverageWidth = textMetrics.tmAveCharWidth;
776 
777  CurrentParameters.SetSectionText = PhMipSetSectionText;
778 
779  SelectObject(hdc, originalFont);
780  ReleaseDC(PhMipWindow, hdc);
781 }
782 
784  _In_ PPH_MINIINFO_SECTION Template
785  )
786 {
787  PPH_MINIINFO_SECTION section;
788 
789  section = PhAllocate(sizeof(PH_MINIINFO_SECTION));
790  memset(section, 0, sizeof(PH_MINIINFO_SECTION));
791 
792  section->Name = Template->Name;
793  section->Flags = Template->Flags;
794  section->Callback = Template->Callback;
795  section->Context = Template->Context;
796  section->Parameters = &CurrentParameters;
797 
798  PhAddItemList(SectionList, section);
799 
800  section->Callback(section, MiniInfoCreate, NULL, NULL);
801 
802  return section;
803 }
804 
806  _In_ PPH_MINIINFO_SECTION Section
807  )
808 {
809  Section->Callback(Section, MiniInfoDestroy, NULL, NULL);
810 
811  PhClearReference(&Section->Text);
812  PhFree(Section);
813 }
814 
816  _In_ PPH_STRINGREF Name
817  )
818 {
819  ULONG i;
820  PPH_MINIINFO_SECTION section;
821 
822  for (i = 0; i < SectionList->Count; i++)
823  {
824  section = SectionList->Items[i];
825 
826  if (PhEqualStringRef(&section->Name, Name, TRUE))
827  return section;
828  }
829 
830  return NULL;
831 }
832 
834  _In_ PWSTR Name,
835  _In_ ULONG Flags,
836  _In_ PPH_MINIINFO_SECTION_CALLBACK Callback
837  )
838 {
839  PH_MINIINFO_SECTION section;
840 
841  memset(&section, 0, sizeof(PH_MINIINFO_SECTION));
842  PhInitializeStringRef(&section.Name, Name);
843  section.Flags = Flags;
844  section.Callback = Callback;
845 
846  return PhMipCreateSection(&section);
847 }
848 
850  _In_ PPH_MINIINFO_SECTION Section
851  )
852 {
853  PH_MINIINFO_CREATE_DIALOG createDialog;
854 
855  memset(&createDialog, 0, sizeof(PH_MINIINFO_CREATE_DIALOG));
856 
857  if (Section->Callback(Section, MiniInfoCreateDialog, &createDialog, NULL))
858  {
859  if (!createDialog.CustomCreate)
860  {
861  Section->DialogHandle = PhCreateDialogFromTemplate(
862  PhMipWindow,
863  DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD,
864  createDialog.Instance,
865  createDialog.Template,
866  createDialog.DialogProc,
867  createDialog.Parameter
868  );
869  }
870  }
871 }
872 
874  _In_ PPH_MINIINFO_SECTION NewSection
875  )
876 {
877  PPH_MINIINFO_SECTION oldSection;
878 
879  if (NewSection == CurrentSection)
880  return;
881 
882  oldSection = CurrentSection;
883  CurrentSection = NewSection;
884 
885  if (oldSection)
886  {
887  oldSection->Callback(oldSection, MiniInfoSectionChanging, CurrentSection, NULL);
888 
889  if (oldSection->DialogHandle)
890  ShowWindow(oldSection->DialogHandle, SW_HIDE);
891  }
892 
893  if (!NewSection->DialogHandle)
894  PhMipCreateSectionDialog(NewSection);
895  if (NewSection->DialogHandle)
896  ShowWindow(NewSection->DialogHandle, SW_SHOW);
897 
898  PhMipUpdateSectionText(NewSection);
899  PhMipLayout();
900 
901  NewSection->Callback(NewSection, MiniInfoTick, NULL, NULL);
902 }
903 
905  _In_ struct _PH_MINIINFO_SECTION *Section,
906  _In_opt_ PPH_STRING Text
907  )
908 {
909  PhSwapReference(&Section->Text, Text);
910 
911  if (Section == CurrentSection)
912  PhMipUpdateSectionText(Section);
913 }
914 
916  _In_ PPH_MINIINFO_SECTION Section
917  )
918 {
919  if (Section->Text)
920  {
921  SetDlgItemText(PhMipWindow, IDC_SECTION, ((PPH_STRING)PhAutoDereferenceObject(
922  PhConcatStringRef2(&DownArrowPrefix, &Section->Text->sr)))->Buffer);
923  }
924  else
925  {
926  SetDlgItemText(PhMipWindow, IDC_SECTION, ((PPH_STRING)PhAutoDereferenceObject(
927  PhConcatStringRef2(&DownArrowPrefix, &Section->Name)))->Buffer);
928  }
929 }
930 
932  VOID
933  )
934 {
935  RECT clientRect;
936  RECT rect;
937 
938  GetClientRect(PhMipContainerWindow, &clientRect);
939  MoveWindow(
940  PhMipWindow,
941  clientRect.left, clientRect.top,
942  clientRect.right - clientRect.left, clientRect.bottom - clientRect.top,
943  FALSE
944  );
945 
946  PhLayoutManagerLayout(&PhMipLayoutManager);
947 
948  GetWindowRect(GetDlgItem(PhMipWindow, IDC_LAYOUT), &rect);
949  MapWindowPoints(NULL, PhMipWindow, (POINT *)&rect, 2);
950 
951  if (CurrentSection && CurrentSection->DialogHandle)
952  {
953  if (CurrentSection->Flags & PH_MINIINFO_SECTION_NO_UPPER_MARGINS)
954  {
955  rect.left = 0;
956  rect.top = 0;
957  rect.right = clientRect.right;
958  }
959  else
960  {
961  LONG leftDistance = rect.left - clientRect.left;
962  LONG rightDistance = clientRect.right - rect.right;
963  LONG minDistance;
964 
965  if (leftDistance != rightDistance)
966  {
967  // HACK: Enforce symmetry. Sometimes these are off by a pixel.
968  minDistance = min(leftDistance, rightDistance);
969  rect.left = clientRect.left + minDistance;
970  rect.right = clientRect.right - minDistance;
971  }
972  }
973 
974  MoveWindow(
975  CurrentSection->DialogHandle,
976  rect.left, rect.top,
977  rect.right - rect.left, rect.bottom - rect.top,
978  TRUE
979  );
980  }
981 
982  GetWindowRect(GetDlgItem(PhMipWindow, IDC_PIN), &rect);
983  MapWindowPoints(NULL, PhMipWindow, (POINT *)&rect, 2);
984 }
985 
987  VOID
988  )
989 {
991 }
992 
994  VOID
995  )
996 {
998  PostMessage(PhMipWindow, WM_MOUSEMOVE, 0, 0); // Re-evaluate hover pin
999 }
1000 
1002  VOID
1003  )
1004 {
1005  if (PhMipPinned)
1007 
1008  PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0);
1009 }
1010 
1012  VOID
1013  )
1014 {
1015  PhMipRefreshAutomatically ^= MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned);
1016  PhSetIntegerSetting(L"MiniInfoWindowRefreshAutomatically", PhMipRefreshAutomatically);
1017 }
1018 
1020  _In_ BOOLEAN Pinned
1021  )
1022 {
1023  PhSetWindowStyle(PhMipContainerWindow, WS_DLGFRAME | WS_SYSMENU, Pinned ? (WS_DLGFRAME | WS_SYSMENU) : 0);
1024  SetWindowPos(PhMipContainerWindow, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
1025  PhMipPinned = Pinned;
1026 
1027  PhNfNotifyMiniInfoPinned(Pinned);
1028 }
1029 
1031  VOID
1032  )
1033 {
1034  PPH_EMENU menu;
1035  ULONG i;
1036  PPH_MINIINFO_SECTION section;
1037  PPH_EMENU_ITEM menuItem;
1038  POINT point;
1039 
1041  menu = PhCreateEMenu();
1042 
1043  for (i = 0; i < SectionList->Count; i++)
1044  {
1045  section = SectionList->Items[i];
1046  menuItem = PhCreateEMenuItem(
1047  (section == CurrentSection ? (PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK) : 0),
1048  0,
1049  ((PPH_STRING)PhAutoDereferenceObject(PhCreateString2(&section->Name)))->Buffer,
1050  NULL,
1051  section
1052  );
1053  PhInsertEMenuItem(menu, menuItem, -1);
1054  }
1055 
1056  GetCursorPos(&point);
1057  menuItem = PhShowEMenu(menu, PhMipWindow, PH_EMENU_SHOW_LEFTRIGHT,
1058  PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y);
1059 
1060  if (menuItem)
1061  {
1062  PhMipChangeSection(menuItem->Context);
1063  }
1064 
1065  PhDestroyEMenu(menu);
1067 }
1068 
1070  VOID
1071  )
1072 {
1073  PPH_EMENU menu;
1074  PPH_EMENU_ITEM menuItem;
1075  ULONG id;
1076  RECT rect;
1077 
1079  menu = PhCreateEMenu();
1080  PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MINIINFO), 0);
1081 
1082  // Opacity
1083 
1084  id = PH_OPACITY_TO_ID(PhGetIntegerSetting(L"MiniInfoWindowOpacity"));
1085 
1086  if (menuItem = PhFindEMenuItem(menu, PH_EMENU_FIND_DESCEND, NULL, id))
1088 
1089  // Refresh Automatically
1090 
1091  if (PhMipRefreshAutomatically & MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned))
1093 
1094  // Show the menu.
1095 
1096  GetWindowRect(GetDlgItem(PhMipWindow, IDC_OPTIONS), &rect);
1097  menuItem = PhShowEMenu(menu, PhMipWindow, PH_EMENU_SHOW_LEFTRIGHT,
1098  PH_ALIGN_LEFT | PH_ALIGN_BOTTOM, rect.left, rect.top);
1099 
1100  if (menuItem)
1101  {
1102  switch (menuItem->Id)
1103  {
1104  case ID_OPACITY_10:
1105  case ID_OPACITY_20:
1106  case ID_OPACITY_30:
1107  case ID_OPACITY_40:
1108  case ID_OPACITY_50:
1109  case ID_OPACITY_60:
1110  case ID_OPACITY_70:
1111  case ID_OPACITY_80:
1112  case ID_OPACITY_90:
1113  case ID_OPACITY_OPAQUE:
1114  {
1115  ULONG opacity;
1116 
1117  opacity = PH_ID_TO_OPACITY(menuItem->Id);
1118  PhSetIntegerSetting(L"MiniInfoWindowOpacity", opacity);
1119  PhSetWindowOpacity(PhMipContainerWindow, opacity);
1120  }
1121  break;
1122  case ID_MINIINFO_REFRESH:
1123  PhMipRefresh();
1124  break;
1127  break;
1128  }
1129  }
1130 
1131  PhDestroyEMenu(menu);
1133 }
1134 
1136  _In_ HWND hwnd,
1137  _In_ UINT uMsg,
1138  _In_ WPARAM wParam,
1139  _In_ LPARAM lParam
1140  )
1141 {
1142  switch (uMsg)
1143  {
1144  case WM_SETCURSOR:
1145  {
1146  SetCursor(LoadCursor(NULL, IDC_HAND));
1147  }
1148  return TRUE;
1149  }
1150 
1151  return CallWindowProc(SectionControlOldWndProc, hwnd, uMsg, wParam, lParam);
1152 }
1153 
1155  _In_ PWSTR Name,
1156  _In_ ULONG Flags,
1157  _In_ PPH_MINIINFO_LIST_SECTION Template
1158  )
1159 {
1160  PPH_MINIINFO_LIST_SECTION listSection;
1161  PH_MINIINFO_SECTION section;
1162 
1163  listSection = PhAllocate(sizeof(PH_MINIINFO_LIST_SECTION));
1164  memset(listSection, 0, sizeof(PH_MINIINFO_LIST_SECTION));
1165 
1166  listSection->Context = Template->Context;
1167  listSection->Callback = Template->Callback;
1168 
1169  memset(&section, 0, sizeof(PH_MINIINFO_SECTION));
1170  PhInitializeStringRef(&section.Name, Name);
1173  section.Context = listSection;
1174  listSection->Section = PhMipCreateSection(&section);
1175 
1176  return listSection;
1177 }
1178 
1180  _In_ PWSTR Name,
1181  _In_ ULONG Flags,
1183  )
1184 {
1185  PH_MINIINFO_LIST_SECTION listSection;
1186 
1187  memset(&listSection, 0, sizeof(PH_MINIINFO_LIST_SECTION));
1188  listSection.Callback = Callback;
1189 
1190  return PhMipCreateListSection(Name, Flags, &listSection);
1191 }
1192 
1194  _In_ PPH_MINIINFO_SECTION Section,
1195  _In_ PH_MINIINFO_SECTION_MESSAGE Message,
1196  _In_opt_ PVOID Parameter1,
1197  _In_opt_ PVOID Parameter2
1198  )
1199 {
1200  PPH_MINIINFO_LIST_SECTION listSection = Section->Context;
1201 
1202  switch (Message)
1203  {
1204  case MiniInfoCreate:
1205  {
1206  listSection->NodeList = PhCreateList(2);
1207  listSection->Callback(listSection, MiListSectionCreate, NULL, NULL);
1208  }
1209  break;
1210  case MiniInfoDestroy:
1211  {
1212  listSection->Callback(listSection, MiListSectionDestroy, NULL, NULL);
1213 
1214  PhMipClearListSection(listSection);
1215  PhDereferenceObject(listSection->NodeList);
1216  PhFree(listSection);
1217  }
1218  break;
1219  case MiniInfoTick:
1220  if (listSection->SuspendUpdate == 0)
1221  PhMipTickListSection(listSection);
1222  break;
1223  case MiniInfoShowing:
1224  {
1225  listSection->Callback(listSection, MiListSectionShowing, Parameter1, Parameter2);
1226 
1227  if (!Parameter1) // Showing
1228  {
1229  // We don't want to hold process item references while the mini info window
1230  // is hidden.
1231  PhMipClearListSection(listSection);
1232  TreeNew_NodesStructured(listSection->TreeNewHandle);
1233  }
1234  }
1235  break;
1236  case MiniInfoCreateDialog:
1237  {
1238  PPH_MINIINFO_CREATE_DIALOG createDialog = Parameter1;
1239 
1240  createDialog->Instance = PhInstanceHandle;
1241  createDialog->Template = MAKEINTRESOURCE(IDD_MINIINFO_LIST);
1242  createDialog->DialogProc = PhMipListSectionDialogProc;
1243  createDialog->Parameter = listSection;
1244  }
1245  return TRUE;
1246  }
1247 
1248  return FALSE;
1249 }
1250 
1252  _In_ HWND hwndDlg,
1253  _In_ UINT uMsg,
1254  _In_ WPARAM wParam,
1255  _In_ LPARAM lParam
1256  )
1257 {
1258  PPH_MINIINFO_LIST_SECTION listSection = (PPH_MINIINFO_LIST_SECTION)GetProp(hwndDlg, PhMakeContextAtom());
1259 
1260  switch (uMsg)
1261  {
1262  case WM_INITDIALOG:
1263  {
1264  PPH_LAYOUT_ITEM layoutItem;
1265 
1266  listSection = (PPH_MINIINFO_LIST_SECTION)lParam;
1267  SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)listSection);
1268 
1269  listSection->DialogHandle = hwndDlg;
1270  listSection->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST);
1271 
1272  PhInitializeLayoutManager(&listSection->LayoutManager, hwndDlg);
1273  layoutItem = PhAddLayoutItem(&listSection->LayoutManager, listSection->TreeNewHandle, NULL, PH_ANCHOR_ALL);
1274 
1275  // Use negative margins to maximize our use of the window area.
1276  layoutItem->Margin.left = -1;
1277  layoutItem->Margin.top = -1;
1278  layoutItem->Margin.right = -1;
1279 
1280  PhSetControlTheme(listSection->TreeNewHandle, L"explorer");
1281  TreeNew_SetCallback(listSection->TreeNewHandle, PhMipListSectionTreeNewCallback, listSection);
1282  TreeNew_SetRowHeight(listSection->TreeNewHandle, PhMipCalculateRowHeight());
1283  PhAddTreeNewColumnEx2(listSection->TreeNewHandle, MIP_SINGLE_COLUMN_ID, TRUE, L"Process", 1,
1285 
1286  listSection->Callback(listSection, MiListSectionDialogCreated, hwndDlg, NULL);
1287  PhMipTickListSection(listSection);
1288  }
1289  break;
1290  case WM_DESTROY:
1291  {
1292  PhDeleteLayoutManager(&listSection->LayoutManager);
1293  RemoveProp(hwndDlg, PhMakeContextAtom());
1294  }
1295  break;
1296  case WM_SIZE:
1297  {
1298  PhLayoutManagerLayout(&listSection->LayoutManager);
1300  }
1301  break;
1302  }
1303 
1304  return FALSE;
1305 }
1306 
1308  _In_ PPH_LIST List,
1309  _In_opt_ PVOID Context
1310  )
1311 {
1312  PPH_MINIINFO_LIST_SECTION listSection = Context;
1314 
1315  sortList.List = List;
1316  listSection->Callback(listSection, MiListSectionSortProcessList, &sortList, NULL);
1317 }
1318 
1320  _In_ PPH_MINIINFO_LIST_SECTION ListSection
1321  )
1322 {
1323  ULONG i;
1324  PPH_MIP_GROUP_NODE node;
1326 
1327  PhMipClearListSection(ListSection);
1328 
1329  ListSection->ProcessGroupList = PhCreateProcessGroupList(
1331  ListSection,
1333  0
1334  );
1335 
1336  if (!ListSection->ProcessGroupList)
1337  return;
1338 
1339  for (i = 0; i < ListSection->ProcessGroupList->Count; i++)
1340  {
1341  node = PhMipAddGroupNode(ListSection, ListSection->ProcessGroupList->Items[i]);
1342 
1343  if (node->RepresentativeProcessId == ListSection->SelectedRepresentativeProcessId &&
1344  node->RepresentativeCreateTime.QuadPart == ListSection->SelectedRepresentativeCreateTime.QuadPart)
1345  {
1346  node->Node.Selected = TRUE;
1347  }
1348 
1349  assignSortData.ProcessGroup = node->ProcessGroup;
1350  assignSortData.SortData = &node->SortData;
1351  ListSection->Callback(ListSection, MiListSectionAssignSortData, &assignSortData, NULL);
1352  }
1353 
1354  TreeNew_NodesStructured(ListSection->TreeNewHandle);
1356 
1357  ListSection->Callback(ListSection, MiListSectionTick, NULL, NULL);
1358 }
1359 
1361  _In_ PPH_MINIINFO_LIST_SECTION ListSection
1362  )
1363 {
1364  ULONG i;
1365 
1366  if (ListSection->ProcessGroupList)
1367  {
1368  PhFreeProcessGroupList(ListSection->ProcessGroupList);
1369  ListSection->ProcessGroupList = NULL;
1370  }
1371 
1372  for (i = 0; i < ListSection->NodeList->Count; i++)
1373  PhMipDestroyGroupNode(ListSection->NodeList->Items[i]);
1374 
1375  PhClearList(ListSection->NodeList);
1376 }
1377 
1379  VOID
1380  )
1381 {
1382  LONG iconHeight;
1383  LONG titleAndSubtitleHeight;
1384 
1386  titleAndSubtitleHeight =
1387  MIP_CELL_PADDING * 2 + CurrentParameters.FontHeight + MIP_INNER_PADDING + CurrentParameters.FontHeight;
1388 
1389  return max(iconHeight, titleAndSubtitleHeight);
1390 }
1391 
1393  _In_ PPH_MINIINFO_LIST_SECTION ListSection,
1394  _In_ PPH_PROCESS_GROUP ProcessGroup
1395  )
1396 {
1397  PPH_MIP_GROUP_NODE node;
1398 
1399  node = PhAllocate(sizeof(PH_MIP_GROUP_NODE));
1400  memset(node, 0, sizeof(PH_MIP_GROUP_NODE));
1401 
1402  PhInitializeTreeNewNode(&node->Node);
1403  node->ProcessGroup = ProcessGroup;
1404  node->RepresentativeProcessId = ProcessGroup->Representative->ProcessId;
1405  node->RepresentativeCreateTime = ProcessGroup->Representative->CreateTime;
1406 
1407  PhAddItemList(ListSection->NodeList, node);
1408 
1409  return node;
1410 }
1411 
1413  _In_ PPH_MIP_GROUP_NODE Node
1414  )
1415 {
1416  PhClearReference(&Node->TooltipText);
1417  PhFree(Node);
1418 }
1419 
1421  _In_ HWND hwnd,
1422  _In_ PH_TREENEW_MESSAGE Message,
1423  _In_opt_ PVOID Parameter1,
1424  _In_opt_ PVOID Parameter2,
1425  _In_opt_ PVOID Context
1426  )
1427 {
1428  PPH_MINIINFO_LIST_SECTION listSection = Context;
1429 
1430  switch (Message)
1431  {
1432  case TreeNewGetChildren:
1433  {
1434  PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
1436 
1437  if (!getChildren->Node)
1438  {
1439  getChildren->Children = (PPH_TREENEW_NODE *)listSection->NodeList->Items;
1440  getChildren->NumberOfChildren = listSection->NodeList->Count;
1441 
1442  sortList.List = listSection->NodeList;
1443  listSection->Callback(listSection, MiListSectionSortGroupList, &sortList, NULL);
1444  }
1445  }
1446  return TRUE;
1447  case TreeNewIsLeaf:
1448  {
1449  PPH_TREENEW_IS_LEAF isLeaf = Parameter1;
1450  isLeaf->IsLeaf = TRUE;
1451  }
1452  return TRUE;
1453  case TreeNewCustomDraw:
1454  {
1455  PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1;
1456  PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)customDraw->Node;
1457  PPH_PROCESS_ITEM processItem = node->ProcessGroup->Representative;
1458  HDC hdc = customDraw->Dc;
1459  RECT rect = customDraw->CellRect;
1460  ULONG baseTextFlags = DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE;
1461  HICON icon;
1462  COLORREF originalTextColor;
1463  RECT topRect;
1464  RECT bottomRect;
1466  ULONG usageTextTopWidth = 0;
1467  ULONG usageTextBottomWidth = 0;
1469 
1470  rect.left += MIP_ICON_PADDING;
1471  rect.top += MIP_ICON_PADDING;
1472  rect.right -= MIP_CELL_PADDING;
1473  rect.bottom -= MIP_CELL_PADDING;
1474 
1475  if (processItem->LargeIcon)
1476  icon = processItem->LargeIcon;
1477  else
1478  PhGetStockApplicationIcon(NULL, &icon);
1479 
1480  DrawIconEx(hdc, rect.left, rect.top, icon, PhLargeIconSize.X, PhLargeIconSize.Y,
1481  0, NULL, DI_NORMAL);
1483  rect.top += MIP_CELL_PADDING - MIP_ICON_PADDING;
1484  SelectObject(hdc, CurrentParameters.Font);
1485 
1486  // This color changes depending on whether the node is selected, etc.
1487  originalTextColor = GetTextColor(hdc);
1488 
1489  // Usage text
1490 
1491  topRect = rect;
1492  topRect.bottom = topRect.top + CurrentParameters.FontHeight;
1493  bottomRect = rect;
1494  bottomRect.top = bottomRect.bottom - CurrentParameters.FontHeight;
1495 
1496  getUsageText.ProcessGroup = node->ProcessGroup;
1497  getUsageText.SortData = &node->SortData;
1498  getUsageText.Line1 = NULL;
1499  getUsageText.Line2 = NULL;
1500  getUsageText.Line1Color = originalTextColor;
1501  getUsageText.Line2Color = originalTextColor;
1502 
1503  if (listSection->Callback(listSection, MiListSectionGetUsageText, &getUsageText, NULL))
1504  {
1505  PH_STRINGREF text;
1506  RECT textRect;
1507  SIZE textSize;
1508 
1509  // Top
1510  text = PhGetStringRef(getUsageText.Line1);
1511  GetTextExtentPoint32(hdc, text.Buffer, (ULONG)text.Length / 2, &textSize);
1512  usageTextTopWidth = textSize.cx;
1513  textRect = topRect;
1514  textRect.left = textRect.right - textSize.cx;
1515  SetTextColor(hdc, getUsageText.Line1Color);
1516  DrawText(hdc, text.Buffer, (ULONG)text.Length / 2, &textRect, baseTextFlags | DT_RIGHT);
1517  PhClearReference(&getUsageText.Line1);
1518 
1519  // Bottom
1520  text = PhGetStringRef(getUsageText.Line2);
1521  GetTextExtentPoint32(hdc, text.Buffer, (ULONG)text.Length / 2, &textSize);
1522  usageTextBottomWidth = textSize.cx;
1523  textRect = bottomRect;
1524  textRect.left = textRect.right - textSize.cx;
1525  SetTextColor(hdc, getUsageText.Line2Color);
1526  DrawText(hdc, text.Buffer, (ULONG)text.Length / 2, &textRect, baseTextFlags | DT_RIGHT);
1527  PhClearReference(&getUsageText.Line2);
1528  }
1529 
1530  // Title, subtitle
1531 
1532  getTitleText.ProcessGroup = node->ProcessGroup;
1533  getTitleText.SortData = &node->SortData;
1534 
1535  if (!PhIsNullOrEmptyString(processItem->VersionInfo.FileDescription))
1536  PhSetReference(&getTitleText.Title, processItem->VersionInfo.FileDescription);
1537  else
1538  PhSetReference(&getTitleText.Title, processItem->ProcessName);
1539 
1540  if (node->ProcessGroup->Processes->Count == 1)
1541  {
1542  PhSetReference(&getTitleText.Subtitle, processItem->ProcessName);
1543  }
1544  else
1545  {
1546  getTitleText.Subtitle = PhFormatString(
1547  L"%s (%u processes)",
1548  processItem->ProcessName->Buffer,
1549  node->ProcessGroup->Processes->Count
1550  );
1551  }
1552 
1553  getTitleText.TitleColor = originalTextColor;
1554  getTitleText.SubtitleColor = GetSysColor(COLOR_GRAYTEXT);
1555 
1556  listSection->Callback(listSection, MiListSectionGetTitleText, &getTitleText, NULL);
1557 
1558  if (!PhIsNullOrEmptyString(getTitleText.Title))
1559  {
1560  RECT textRect;
1561 
1562  textRect = topRect;
1563  textRect.right -= usageTextTopWidth + MIP_INNER_PADDING;
1564  SetTextColor(hdc, getTitleText.TitleColor);
1565  DrawText(
1566  hdc,
1567  getTitleText.Title->Buffer,
1568  (ULONG)getTitleText.Title->Length / 2,
1569  &textRect,
1570  baseTextFlags | DT_END_ELLIPSIS
1571  );
1572  }
1573 
1574  if (!PhIsNullOrEmptyString(getTitleText.Subtitle))
1575  {
1576  RECT textRect;
1577 
1578  textRect = bottomRect;
1579  textRect.right -= usageTextBottomWidth + MIP_INNER_PADDING;
1580  SetTextColor(hdc, getTitleText.SubtitleColor);
1581  DrawText(
1582  hdc,
1583  getTitleText.Subtitle->Buffer,
1584  (ULONG)getTitleText.Subtitle->Length / 2,
1585  &textRect,
1586  baseTextFlags | DT_END_ELLIPSIS
1587  );
1588  }
1589 
1590  PhClearReference(&getTitleText.Title);
1591  PhClearReference(&getTitleText.Subtitle);
1592  }
1593  return TRUE;
1594  case TreeNewGetCellTooltip:
1595  {
1596  PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1;
1597  PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)getCellTooltip->Node;
1598  ULONG tickCount;
1599 
1600  tickCount = GetTickCount();
1601 
1602  // This is useless most of the time because the tooltip doesn't display unless the window is active.
1603  // TODO: Find a way to make the tooltip display all the time.
1604 
1605  if (!node->TooltipText)
1606  node->TooltipText = PhMipGetGroupNodeTooltip(listSection, node);
1607 
1608  if (!PhIsNullOrEmptyString(node->TooltipText))
1609  {
1610  getCellTooltip->Text = node->TooltipText->sr;
1611  getCellTooltip->Unfolding = FALSE;
1612  getCellTooltip->MaximumWidth = -1;
1613  }
1614  else
1615  {
1616  return FALSE;
1617  }
1618  }
1619  return TRUE;
1621  {
1622  ULONG i;
1623  PPH_MIP_GROUP_NODE node;
1624 
1625  listSection->SelectedRepresentativeProcessId = NULL;
1626  listSection->SelectedRepresentativeCreateTime.QuadPart = 0;
1627 
1628  for (i = 0; i < listSection->NodeList->Count; i++)
1629  {
1630  node = listSection->NodeList->Items[i];
1631 
1632  if (node->Node.Selected)
1633  {
1636  break;
1637  }
1638  }
1639  }
1640  break;
1641  case TreeNewKeyDown:
1642  {
1643  PPH_TREENEW_KEY_EVENT keyEvent = Parameter1;
1644  PPH_MIP_GROUP_NODE node;
1645 
1646  listSection->SuspendUpdate++;
1647 
1648  switch (keyEvent->VirtualKey)
1649  {
1650  case VK_DELETE:
1651  if (node = PhMipGetSelectedGroupNode(listSection))
1653  break;
1654  case VK_RETURN:
1655  if (node = PhMipGetSelectedGroupNode(listSection))
1656  {
1657  if (GetKeyState(VK_CONTROL) >= 0)
1658  {
1660  }
1661  else
1662  {
1663  if (node->ProcessGroup->Representative->FileName)
1665  }
1666  }
1667  break;
1668  }
1669 
1670  listSection->SuspendUpdate--;
1671  }
1672  return TRUE;
1674  {
1675  PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1;
1676  PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)mouseEvent->Node;
1677 
1678  if (node)
1679  {
1680  listSection->SuspendUpdate++;
1682  listSection->SuspendUpdate--;
1683  }
1684  }
1685  break;
1686  case TreeNewContextMenu:
1687  {
1688  PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1;
1689 
1690  // Prevent the node list from being updated (otherwise any nodes we're using might be destroyed while we're
1691  // in a modal message loop).
1692  listSection->SuspendUpdate++;
1694  PhMipShowListSectionContextMenu(listSection, contextMenu);
1696  listSection->SuspendUpdate--;
1697  }
1698  break;
1699  }
1700 
1701  return FALSE;
1702 }
1703 
1705  _In_ PPH_MINIINFO_LIST_SECTION ListSection,
1706  _In_ PPH_MIP_GROUP_NODE Node
1707  )
1708 {
1709  PH_STRING_BUILDER sb;
1710 
1711  PhInitializeStringBuilder(&sb, 100);
1712 
1713  // TODO
1714 
1715  return PhFinalStringBuilderString(&sb);
1716 }
1717 
1719  _In_ PPH_MINIINFO_LIST_SECTION ListSection
1720  )
1721 {
1722  ULONG i;
1723  PPH_MIP_GROUP_NODE node;
1724 
1725  for (i = 0; i < ListSection->NodeList->Count; i++)
1726  {
1727  node = ListSection->NodeList->Items[i];
1728 
1729  if (node->Node.Selected)
1730  return node;
1731  }
1732 
1733  return NULL;
1734 }
1735 
1737  _In_ PPH_MINIINFO_LIST_SECTION ListSection,
1738  _In_ PPH_TREENEW_CONTEXT_MENU ContextMenu
1739  )
1740 {
1741  PPH_MIP_GROUP_NODE selectedNode;
1742  PPH_EMENU menu;
1743  PPH_EMENU_ITEM item;
1745  PH_PLUGIN_MENU_INFORMATION pluginMenuInfo;
1746 
1747  selectedNode = PhMipGetSelectedGroupNode(ListSection);
1748 
1749  if (!selectedNode)
1750  return;
1751 
1752  menu = PhCreateEMenu();
1753  // TODO: If there are multiple processes, then create submenus for each process.
1754  PhAddMiniProcessMenuItems(menu, ListSection->SelectedRepresentativeProcessId);
1757 
1758  if (selectedNode->ProcessGroup->Processes->Count != 1)
1759  {
1760  if (item = PhFindEMenuItem(menu, 0, NULL, ID_PROCESS_GOTOPROCESS))
1761  PhModifyEMenuItem(item, PH_EMENU_MODIFY_TEXT, 0, L"&Go to Processes", NULL);
1762  }
1763 
1764  memset(&menuInfo, 0, sizeof(PH_MINIINFO_LIST_SECTION_MENU_INFORMATION));
1765  menuInfo.ProcessGroup = selectedNode->ProcessGroup;
1766  menuInfo.SortData = &selectedNode->SortData;
1767  menuInfo.ContextMenu = ContextMenu;
1768  ListSection->Callback(ListSection, MiListSectionInitializeContextMenu, &menuInfo, NULL);
1769 
1770  if (PhPluginsEnabled)
1771  {
1772  PhPluginInitializeMenuInfo(&pluginMenuInfo, menu, ListSection->DialogHandle, 0);
1773  pluginMenuInfo.Menu = menu;
1774  pluginMenuInfo.OwnerWindow = PhMipWindow;
1775  pluginMenuInfo.u.MiListSection.SectionName = &ListSection->Section->Name;
1776  pluginMenuInfo.u.MiListSection.ProcessGroup = selectedNode->ProcessGroup;
1777 
1779  }
1780 
1781  item = PhShowEMenu(
1782  menu,
1783  PhMipWindow,
1786  ContextMenu->Location.x,
1787  ContextMenu->Location.y
1788  );
1789 
1790  if (item)
1791  {
1792  BOOLEAN handled = FALSE;
1793 
1794  if (!handled && PhPluginsEnabled)
1795  handled = PhPluginTriggerEMenuItem(&pluginMenuInfo, item);
1796 
1797  if (!handled)
1798  {
1799  menuInfo.SelectedItem = item;
1800  handled = ListSection->Callback(ListSection, MiListSectionHandleContextMenu, &menuInfo, NULL);
1801  }
1802 
1803  if (!handled)
1805 
1806  if (!handled)
1807  PhMipHandleListSectionCommand(ListSection, selectedNode->ProcessGroup, item->Id);
1808  }
1809 
1810  PhDestroyEMenu(menu);
1811 }
1812 
1814  _In_ PPH_MINIINFO_LIST_SECTION ListSection,
1815  _In_ PPH_PROCESS_GROUP ProcessGroup,
1816  _In_ ULONG Id
1817  )
1818 {
1819  switch (Id)
1820  {
1822  {
1823  PPH_LIST nodes;
1824  ULONG i;
1825 
1826  nodes = PhCreateList(ProcessGroup->Processes->Count);
1827 
1828  for (i = 0; i < ProcessGroup->Processes->Count; i++)
1829  {
1830  PPH_PROCESS_NODE node;
1831 
1832  if (node = PhFindProcessNode(((PPH_PROCESS_ITEM)ProcessGroup->Processes->Items[i])->ProcessId))
1833  PhAddItemList(nodes, node);
1834  }
1835 
1836  PhPinMiniInformation(MiniInfoIconPinType, -1, 0, 0, NULL, NULL);
1837  PhPinMiniInformation(MiniInfoActivePinType, -1, 0, 0, NULL, NULL);
1838  PhPinMiniInformation(MiniInfoHoverPinType, -1, 0, 0, NULL, NULL);
1839 
1843  PhDereferenceObject(nodes);
1844  }
1845  break;
1846  }
1847 }
1848 
1850  _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection,
1851  _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
1852  _In_opt_ PVOID Parameter1,
1853  _In_opt_ PVOID Parameter2
1854  )
1855 {
1856  switch (Message)
1857  {
1858  case MiListSectionTick:
1859  ListSection->Section->Parameters->SetSectionText(ListSection->Section,
1860  PhaFormatString(L"CPU %.2f%%", (PhCpuUserUsage + PhCpuKernelUsage) * 100));
1861  break;
1863  {
1864  PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1;
1865 
1866  qsort(sortList->List->Items, sortList->List->Count,
1868  }
1869  return TRUE;
1871  {
1872  PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1;
1873  PPH_LIST processes = assignSortData->ProcessGroup->Processes;
1874  FLOAT cpuUsage = 0;
1875  ULONG i;
1876 
1877  for (i = 0; i < processes->Count; i++)
1878  cpuUsage += ((PPH_PROCESS_ITEM)processes->Items[i])->CpuUsage;
1879 
1880  *(PFLOAT)assignSortData->SortData->UserData = cpuUsage;
1881  }
1882  return TRUE;
1884  {
1885  PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1;
1886 
1887  qsort(sortList->List->Items, sortList->List->Count,
1889  }
1890  return TRUE;
1892  {
1893  PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1;
1894  PPH_LIST processes = getUsageText->ProcessGroup->Processes;
1895  FLOAT cpuUsage = *(PFLOAT)getUsageText->SortData->UserData * 100;
1896  PPH_STRING cpuUsageText;
1897 
1898  if (cpuUsage >= 0.01)
1899  cpuUsageText = PhFormatString(L"%.2f%%", cpuUsage);
1900  else if (cpuUsage != 0)
1901  cpuUsageText = PhCreateString(L"< 0.01%");
1902  else
1903  cpuUsageText = NULL;
1904 
1905  PhMoveReference(&getUsageText->Line1, cpuUsageText);
1906  }
1907  return TRUE;
1908  }
1909 
1910  return FALSE;
1911 }
1912 
1914  _In_ const void *elem1,
1915  _In_ const void *elem2
1916  )
1917 {
1918  int result;
1919  PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1;
1920  PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2;
1921 
1922  result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage);
1923 
1924  if (result == 0)
1925  result = uint64cmp(node2->ProcessItem->UserTime.QuadPart, node1->ProcessItem->UserTime.QuadPart);
1926 
1927  return result;
1928 }
1929 
1931  _In_ const void *elem1,
1932  _In_ const void *elem2
1933  )
1934 {
1937 
1938  return singlecmp(*(PFLOAT)data2->UserData, *(PFLOAT)data1->UserData);
1939 }
1940 
1942  _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection,
1943  _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
1944  _In_opt_ PVOID Parameter1,
1945  _In_opt_ PVOID Parameter2
1946  )
1947 {
1948  switch (Message)
1949  {
1950  case MiListSectionTick:
1951  {
1952  PH_FORMAT format[5];
1953  DOUBLE commitFraction = (DOUBLE)PhPerfInformation.CommittedPages / PhPerfInformation.CommitLimit;
1954 
1955  PhInitFormatS(&format[0], L"Commit ");
1956  PhInitFormatSize(&format[1], UInt32x32To64(PhPerfInformation.CommittedPages, PAGE_SIZE));
1957  PhInitFormatS(&format[2], L" (");
1958  PhInitFormatF(&format[3], commitFraction * 100, 2);
1959  PhInitFormatS(&format[4], L"%)");
1960  ListSection->Section->Parameters->SetSectionText(ListSection->Section,
1961  PhAutoDereferenceObject(PhFormat(format, 5, 96)));
1962  }
1963  break;
1965  {
1966  PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1;
1967 
1968  qsort(sortList->List->Items, sortList->List->Count,
1970  }
1971  return TRUE;
1973  {
1974  PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1;
1975  PPH_LIST processes = assignSortData->ProcessGroup->Processes;
1976  ULONG64 privateBytes = 0;
1977  ULONG i;
1978 
1979  for (i = 0; i < processes->Count; i++)
1980  privateBytes += ((PPH_PROCESS_ITEM)processes->Items[i])->VmCounters.PagefileUsage;
1981 
1982  *(PULONG64)assignSortData->SortData->UserData = privateBytes;
1983  }
1984  return TRUE;
1986  {
1987  PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1;
1988 
1989  qsort(sortList->List->Items, sortList->List->Count,
1991  }
1992  return TRUE;
1994  {
1995  PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1;
1996  PPH_LIST processes = getUsageText->ProcessGroup->Processes;
1997  ULONG64 privateBytes = *(PULONG64)getUsageText->SortData->UserData;
1998 
1999  PhMoveReference(&getUsageText->Line1, PhFormatSize(privateBytes, -1));
2000  PhMoveReference(&getUsageText->Line2, PhCreateString(L"Private Bytes"));
2001  getUsageText->Line2Color = GetSysColor(COLOR_GRAYTEXT);
2002  }
2003  return TRUE;
2004  }
2005 
2006  return FALSE;
2007 }
2008 
2010  _In_ const void *elem1,
2011  _In_ const void *elem2
2012  )
2013 {
2014  PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1;
2015  PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2;
2016 
2017  return uintptrcmp(node2->ProcessItem->VmCounters.PagefileUsage, node1->ProcessItem->VmCounters.PagefileUsage);
2018 }
2019 
2021  _In_ const void *elem1,
2022  _In_ const void *elem2
2023  )
2024 {
2027 
2028  return uint64cmp(*(PULONG64)data2->UserData, *(PULONG64)data1->UserData);
2029 }
2030 
2032  _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection,
2033  _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
2034  _In_opt_ PVOID Parameter1,
2035  _In_opt_ PVOID Parameter2
2036  )
2037 {
2038  switch (Message)
2039  {
2040  case MiListSectionTick:
2041  {
2042  PH_FORMAT format[5];
2044  FLOAT physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages;
2045 
2046  PhInitFormatS(&format[0], L"Physical ");
2047  PhInitFormatSize(&format[1], UInt32x32To64(physicalUsage, PAGE_SIZE));
2048  PhInitFormatS(&format[2], L" (");
2049  PhInitFormatF(&format[3], physicalFraction * 100, 2);
2050  PhInitFormatS(&format[4], L"%)");
2051  ListSection->Section->Parameters->SetSectionText(ListSection->Section,
2052  PhAutoDereferenceObject(PhFormat(format, 5, 96)));
2053  }
2054  break;
2056  {
2057  PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1;
2058 
2059  qsort(sortList->List->Items, sortList->List->Count,
2061  }
2062  return TRUE;
2064  {
2065  PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1;
2066  PPH_LIST processes = assignSortData->ProcessGroup->Processes;
2067  ULONG64 workingSet = 0;
2068  ULONG i;
2069 
2070  for (i = 0; i < processes->Count; i++)
2071  workingSet += ((PPH_PROCESS_ITEM)processes->Items[i])->VmCounters.WorkingSetSize;
2072 
2073  *(PULONG64)assignSortData->SortData->UserData = workingSet;
2074  }
2075  return TRUE;
2077  {
2078  PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1;
2079 
2080  qsort(sortList->List->Items, sortList->List->Count,
2082  }
2083  return TRUE;
2085  {
2086  PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1;
2087  PPH_LIST processes = getUsageText->ProcessGroup->Processes;
2088  ULONG64 privateBytes = *(PULONG64)getUsageText->SortData->UserData;
2089 
2090  PhMoveReference(&getUsageText->Line1, PhFormatSize(privateBytes, -1));
2091  PhMoveReference(&getUsageText->Line2, PhCreateString(L"Working Set"));
2092  getUsageText->Line2Color = GetSysColor(COLOR_GRAYTEXT);
2093  }
2094  return TRUE;
2095  }
2096 
2097  return FALSE;
2098 }
2099 
2101  _In_ const void *elem1,
2102  _In_ const void *elem2
2103  )
2104 {
2105  PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1;
2106  PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2;
2107 
2108  return uintptrcmp(node2->ProcessItem->VmCounters.WorkingSetSize, node1->ProcessItem->VmCounters.WorkingSetSize);
2109 }
2110 
2112  _In_ const void *elem1,
2113  _In_ const void *elem2
2114  )
2115 {
2118 
2119  return uint64cmp(*(PULONG64)data2->UserData, *(PULONG64)data1->UserData);
2120 }
2121 
2123  _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection,
2124  _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message,
2125  _In_opt_ PVOID Parameter1,
2126  _In_opt_ PVOID Parameter2
2127  )
2128 {
2129  switch (Message)
2130  {
2131  case MiListSectionTick:
2132  {
2133  PH_FORMAT format[6];
2134 
2135  PhInitFormatS(&format[0], L"I/O R: ");
2136  PhInitFormatSize(&format[1], PhIoReadDelta.Delta);
2137  format[1].Type |= FormatUsePrecision;
2138  format[1].Precision = 0;
2139  PhInitFormatS(&format[2], L" W: ");
2140  PhInitFormatSize(&format[3], PhIoWriteDelta.Delta);
2141  format[3].Type |= FormatUsePrecision;
2142  format[3].Precision = 0;
2143  PhInitFormatS(&format[4], L" O: ");
2144  PhInitFormatSize(&format[5], PhIoOtherDelta.Delta);
2145  format[5].Type |= FormatUsePrecision;
2146  format[5].Precision = 0;
2147  ListSection->Section->Parameters->SetSectionText(ListSection->Section,
2148  PhAutoDereferenceObject(PhFormat(format, 6, 80)));
2149  }
2150  break;
2152  {
2153  PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1;
2154 
2155  qsort(sortList->List->Items, sortList->List->Count,
2157  }
2158  return TRUE;
2160  {
2161  PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1;
2162  PPH_LIST processes = assignSortData->ProcessGroup->Processes;
2163  ULONG64 ioReadOtherDelta = 0;
2164  ULONG64 ioWriteDelta = 0;
2165  ULONG i;
2166 
2167  for (i = 0; i < processes->Count; i++)
2168  {
2169  PPH_PROCESS_ITEM processItem = processes->Items[i];
2170  ioReadOtherDelta += processItem->IoReadDelta.Delta;
2171  ioWriteDelta += processItem->IoWriteDelta.Delta;
2172  ioReadOtherDelta += processItem->IoOtherDelta.Delta;
2173  }
2174 
2175  assignSortData->SortData->UserData[0] = ioReadOtherDelta;
2176  assignSortData->SortData->UserData[1] = ioWriteDelta;
2177  }
2178  return TRUE;
2180  {
2181  PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1;
2182 
2183  qsort(sortList->List->Items, sortList->List->Count,
2185  }
2186  return TRUE;
2188  {
2189  PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1;
2190  PPH_LIST processes = getUsageText->ProcessGroup->Processes;
2191  ULONG64 ioReadOtherDelta = getUsageText->SortData->UserData[0];
2192  ULONG64 ioWriteDelta = getUsageText->SortData->UserData[1];
2193  PH_FORMAT format[1];
2194 
2195  PhInitFormatSize(&format[0], ioReadOtherDelta + ioWriteDelta);
2196  PhMoveReference(&getUsageText->Line1, PhFormat(format, 1, 16));
2197  }
2198  return TRUE;
2199  }
2200 
2201  return FALSE;
2202 }
2203 
2205  _In_ const void *elem1,
2206  _In_ const void *elem2
2207  )
2208 {
2209  int result;
2210  PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1;
2211  PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2;
2212  ULONG64 delta1 = node1->ProcessItem->IoReadDelta.Delta + node1->ProcessItem->IoWriteDelta.Delta + node1->ProcessItem->IoOtherDelta.Delta;
2213  ULONG64 delta2 = node2->ProcessItem->IoReadDelta.Delta + node2->ProcessItem->IoWriteDelta.Delta + node2->ProcessItem->IoOtherDelta.Delta;
2214  ULONG64 value1 = node1->ProcessItem->IoReadDelta.Value + node1->ProcessItem->IoWriteDelta.Value + node1->ProcessItem->IoOtherDelta.Value;
2215  ULONG64 value2 = node2->ProcessItem->IoReadDelta.Value + node2->ProcessItem->IoWriteDelta.Value + node2->ProcessItem->IoOtherDelta.Value;
2216 
2217  result = uint64cmp(delta2, delta1);
2218 
2219  if (result == 0)
2220  result = uint64cmp(value2, value1);
2221 
2222  return result;
2223 }
2224 
2226  _In_ const void *elem1,
2227  _In_ const void *elem2
2228  )
2229 {
2232 
2233  return uint64cmp(data2->UserData[0] + data2->UserData[1], data1->UserData[0] + data1->UserData[1]);
2234 }