Process Hacker
disktab.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Extended Tools -
3  * ETW disk monitoring
4  *
5  * Copyright (C) 2011-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 "exttools.h"
24 #include "etwmon.h"
25 #include "resource.h"
26 #include <toolstatusintf.h>
27 #include "disktabp.h"
28 
29 static BOOLEAN DiskTreeNewCreated = FALSE;
30 static HWND DiskTreeNewHandle;
31 static ULONG DiskTreeNewSortColumn;
32 static PH_SORT_ORDER DiskTreeNewSortOrder;
33 
34 static PPH_HASHTABLE DiskNodeHashtable; // hashtable of all nodes
35 static PPH_LIST DiskNodeList; // list of all nodes
36 
37 static PH_CALLBACK_REGISTRATION DiskItemAddedRegistration;
38 static PH_CALLBACK_REGISTRATION DiskItemModifiedRegistration;
39 static PH_CALLBACK_REGISTRATION DiskItemRemovedRegistration;
40 static PH_CALLBACK_REGISTRATION DiskItemsUpdatedRegistration;
41 static BOOLEAN DiskNeedsRedraw = FALSE;
42 
43 static PH_TN_FILTER_SUPPORT FilterSupport;
44 static PTOOLSTATUS_INTERFACE ToolStatusInterface;
45 static PH_CALLBACK_REGISTRATION SearchChangedRegistration;
46 
48  VOID
49  )
50 {
51  PH_ADDITIONAL_TAB_PAGE tabPage;
52  PPH_ADDITIONAL_TAB_PAGE addedTabPage;
53  PPH_PLUGIN toolStatusPlugin;
54 
55  if (toolStatusPlugin = PhFindPlugin(TOOLSTATUS_PLUGIN_NAME))
56  {
57  ToolStatusInterface = PhGetPluginInformation(toolStatusPlugin)->Interface;
58 
59  if (ToolStatusInterface->Version < TOOLSTATUS_INTERFACE_VERSION)
60  ToolStatusInterface = NULL;
61  }
62 
63  memset(&tabPage, 0, sizeof(PH_ADDITIONAL_TAB_PAGE));
64  tabPage.Text = L"Disk";
66  tabPage.Index = MAXINT;
70  addedTabPage = ProcessHacker_AddTabPage(PhMainWndHandle, &tabPage);
71 
72  if (ToolStatusInterface)
73  {
74  PTOOLSTATUS_TAB_INFO tabInfo;
75 
76  tabInfo = ToolStatusInterface->RegisterTabInfo(addedTabPage->Index);
77  tabInfo->BannerText = L"Search Disk";
80  }
81 }
82 
84  _In_ PVOID Context
85  )
86 {
87  HWND hwnd;
88 
89  if (EtEtwEnabled)
90  {
91  ULONG thinRows;
92 
93  thinRows = PhGetIntegerSetting(L"ThinRows") ? TN_STYLE_THIN_ROWS : 0;
94  hwnd = CreateWindow(
96  NULL,
97  WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_BORDER | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows,
98  0,
99  0,
100  3,
101  3,
103  NULL,
104  NULL,
105  NULL
106  );
107 
108  if (!hwnd)
109  return NULL;
110  }
111  else
112  {
113  return CreateDialog(
115  MAKEINTRESOURCE(IDD_DISKTABERROR),
118  );
119  }
120 
121  DiskTreeNewCreated = TRUE;
122 
123  DiskNodeHashtable = PhCreateHashtable(
124  sizeof(PET_DISK_NODE),
127  100
128  );
129  DiskNodeList = PhCreateList(100);
130 
132 
136  NULL,
137  &DiskItemAddedRegistration
138  );
142  NULL,
143  &DiskItemModifiedRegistration
144  );
148  NULL,
149  &DiskItemRemovedRegistration
150  );
154  NULL,
155  &DiskItemsUpdatedRegistration
156  );
157 
158  SetCursor(LoadCursor(NULL, IDC_WAIT));
160  SetCursor(LoadCursor(NULL, IDC_ARROW));
161 
162  return hwnd;
163 }
164 
166  _In_ PVOID Parameter1,
167  _In_ PVOID Parameter2,
168  _In_ PVOID Parameter3,
169  _In_ PVOID Context
170  )
171 {
172  if ((BOOLEAN)Parameter1)
173  {
174  if (DiskTreeNewHandle)
175  SetFocus(DiskTreeNewHandle);
176  }
177 }
178 
180  _In_ PVOID Parameter1,
181  _In_ PVOID Parameter2,
182  _In_ PVOID Parameter3,
183  _In_ PVOID Context
184  )
185 {
186  PPH_FILE_STREAM fileStream = Parameter1;
187  ULONG mode = PtrToUlong(Parameter2);
188 
189  if (!EtEtwEnabled)
190  return;
191 
192  EtWriteDiskList(fileStream, mode);
193 }
194 
196  _In_ PVOID Parameter1,
197  _In_ PVOID Parameter2,
198  _In_ PVOID Parameter3,
199  _In_ PVOID Context
200  )
201 {
202  if (DiskTreeNewHandle)
203  SendMessage(DiskTreeNewHandle, WM_SETFONT, (WPARAM)Parameter1, TRUE);
204 }
205 
207  _In_ PVOID Entry1,
208  _In_ PVOID Entry2
209  )
210 {
211  PET_DISK_NODE diskNode1 = *(PET_DISK_NODE *)Entry1;
212  PET_DISK_NODE diskNode2 = *(PET_DISK_NODE *)Entry2;
213 
214  return diskNode1->DiskItem == diskNode2->DiskItem;
215 }
216 
218  _In_ PVOID Entry
219  )
220 {
221  return PhHashIntPtr((ULONG_PTR)(*(PET_DISK_NODE *)Entry)->DiskItem);
222 }
223 
225  _In_ HWND hwnd
226  )
227 {
228  DiskTreeNewHandle = hwnd;
229  PhSetControlTheme(DiskTreeNewHandle, L"explorer");
230  SendMessage(TreeNew_GetTooltips(DiskTreeNewHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, 0x7fff);
231 
233 
234  TreeNew_SetRedraw(hwnd, FALSE);
235 
236  // Default columns
237  PhAddTreeNewColumn(hwnd, ETDSTNC_NAME, TRUE, L"Name", 100, PH_ALIGN_LEFT, 0, 0);
238  PhAddTreeNewColumn(hwnd, ETDSTNC_FILE, TRUE, L"File", 400, PH_ALIGN_LEFT, 1, DT_PATH_ELLIPSIS);
239  PhAddTreeNewColumnEx(hwnd, ETDSTNC_READRATEAVERAGE, TRUE, L"Read Rate Average", 70, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE);
240  PhAddTreeNewColumnEx(hwnd, ETDSTNC_WRITERATEAVERAGE, TRUE, L"Write Rate Average", 70, PH_ALIGN_RIGHT, 3, DT_RIGHT, TRUE);
241  PhAddTreeNewColumnEx(hwnd, ETDSTNC_TOTALRATEAVERAGE, TRUE, L"Total Rate Average", 70, PH_ALIGN_RIGHT, 4, DT_RIGHT, TRUE);
242  PhAddTreeNewColumnEx(hwnd, ETDSTNC_IOPRIORITY, TRUE, L"I/O Priority", 70, PH_ALIGN_LEFT, 5, 0, TRUE);
243  PhAddTreeNewColumnEx(hwnd, ETDSTNC_RESPONSETIME, TRUE, L"Response Time (ms)", 70, PH_ALIGN_RIGHT, 6, 0, TRUE);
244 
245  TreeNew_SetRedraw(hwnd, TRUE);
246 
248 
250 
251  PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, DiskNodeList);
252 
253  if (ToolStatusInterface)
254  {
255  PhRegisterCallback(ToolStatusInterface->SearchChangedEvent, EtpSearchChangedHandler, NULL, &SearchChangedRegistration);
257  }
258 }
259 
261  VOID
262  )
263 {
264  PPH_STRING settings;
265  PH_INTEGER_PAIR sortSettings;
266 
268  PhCmLoadSettings(DiskTreeNewHandle, &settings->sr);
269  PhDereferenceObject(settings);
270 
272  TreeNew_SetSort(DiskTreeNewHandle, (ULONG)sortSettings.X, (PH_SORT_ORDER)sortSettings.Y);
273 }
274 
276  VOID
277  )
278 {
279  PPH_STRING settings;
280  PH_INTEGER_PAIR sortSettings;
281  ULONG sortColumn;
282  PH_SORT_ORDER sortOrder;
283 
284  if (!DiskTreeNewCreated)
285  return;
286 
287  settings = PhCmSaveSettings(DiskTreeNewHandle);
289  PhDereferenceObject(settings);
290 
291  TreeNew_GetSort(DiskTreeNewHandle, &sortColumn, &sortOrder);
292  sortSettings.X = sortColumn;
293  sortSettings.Y = sortOrder;
295 }
296 
298  _In_ PET_DISK_ITEM DiskItem
299  )
300 {
301  PET_DISK_NODE diskNode;
302 
303  diskNode = PhAllocate(sizeof(ET_DISK_NODE));
304  memset(diskNode, 0, sizeof(ET_DISK_NODE));
305  PhInitializeTreeNewNode(&diskNode->Node);
306 
307  PhSetReference(&diskNode->DiskItem, DiskItem);
308 
309  memset(diskNode->TextCache, 0, sizeof(PH_STRINGREF) * ETDSTNC_MAXIMUM);
310  diskNode->Node.TextCache = diskNode->TextCache;
311  diskNode->Node.TextCacheSize = ETDSTNC_MAXIMUM;
312 
313  diskNode->ProcessNameText = EtpGetDiskItemProcessName(DiskItem);
314 
315  PhAddEntryHashtable(DiskNodeHashtable, &diskNode);
316  PhAddItemList(DiskNodeList, diskNode);
317 
318  if (FilterSupport.NodeList)
319  diskNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &diskNode->Node);
320 
321  TreeNew_NodesStructured(DiskTreeNewHandle);
322 
323  return diskNode;
324 }
325 
327  _In_ PET_DISK_ITEM DiskItem
328  )
329 {
330  ET_DISK_NODE lookupDiskNode;
331  PET_DISK_NODE lookupDiskNodePtr = &lookupDiskNode;
332  PET_DISK_NODE *diskNode;
333 
334  lookupDiskNode.DiskItem = DiskItem;
335 
336  diskNode = (PET_DISK_NODE *)PhFindEntryHashtable(
337  DiskNodeHashtable,
338  &lookupDiskNodePtr
339  );
340 
341  if (diskNode)
342  return *diskNode;
343  else
344  return NULL;
345 }
346 
348  _In_ PET_DISK_NODE DiskNode
349  )
350 {
351  ULONG index;
352 
353  // Remove from the hashtable/list and cleanup.
354 
355  PhRemoveEntryHashtable(DiskNodeHashtable, &DiskNode);
356 
357  if ((index = PhFindItemList(DiskNodeList, DiskNode)) != -1)
358  PhRemoveItemList(DiskNodeList, index);
359 
360  if (DiskNode->ProcessNameText) PhDereferenceObject(DiskNode->ProcessNameText);
361  if (DiskNode->ReadRateAverageText) PhDereferenceObject(DiskNode->ReadRateAverageText);
362  if (DiskNode->WriteRateAverageText) PhDereferenceObject(DiskNode->WriteRateAverageText);
363  if (DiskNode->TotalRateAverageText) PhDereferenceObject(DiskNode->TotalRateAverageText);
364  if (DiskNode->ResponseTimeText) PhDereferenceObject(DiskNode->ResponseTimeText);
365  if (DiskNode->TooltipText) PhDereferenceObject(DiskNode->TooltipText);
366 
367  PhDereferenceObject(DiskNode->DiskItem);
368 
369  PhFree(DiskNode);
370 
371  TreeNew_NodesStructured(DiskTreeNewHandle);
372 }
373 
375  _In_ PET_DISK_NODE DiskNode
376  )
377 {
378  memset(DiskNode->TextCache, 0, sizeof(PH_STRINGREF) * ETDSTNC_MAXIMUM);
379 
380  PhInvalidateTreeNewNode(&DiskNode->Node, TN_CACHE_ICON);
381  TreeNew_NodesStructured(DiskTreeNewHandle);
382 }
383 
384 #define SORT_FUNCTION(Column) EtpDiskTreeNewCompare##Column
385 
386 #define BEGIN_SORT_FUNCTION(Column) static int __cdecl EtpDiskTreeNewCompare##Column( \
387  _In_ const void *_elem1, \
388  _In_ const void *_elem2 \
389  ) \
390 { \
391  PET_DISK_NODE node1 = *(PET_DISK_NODE *)_elem1; \
392  PET_DISK_NODE node2 = *(PET_DISK_NODE *)_elem2; \
393  PET_DISK_ITEM diskItem1 = node1->DiskItem; \
394  PET_DISK_ITEM diskItem2 = node2->DiskItem; \
395  int sortResult = 0;
396 
397 #define END_SORT_FUNCTION \
398  if (sortResult == 0) \
399  sortResult = PhCompareString(diskItem1->FileNameWin32, diskItem2->FileNameWin32, TRUE); \
400  \
401  return PhModifySort(sortResult, DiskTreeNewSortOrder); \
402 }
403 
405 {
406  sortResult = PhCompareString(node1->ProcessNameText, node2->ProcessNameText, TRUE);
407 }
409 
411 {
412  sortResult = PhCompareString(diskItem1->FileNameWin32, diskItem2->FileNameWin32, TRUE);
413 }
415 
416 BEGIN_SORT_FUNCTION(ReadRateAverage)
417 {
418  sortResult = uint64cmp(diskItem1->ReadAverage, diskItem2->ReadAverage);
419 }
421 
422 BEGIN_SORT_FUNCTION(WriteRateAverage)
423 {
424  sortResult = uint64cmp(diskItem1->WriteAverage, diskItem2->WriteAverage);
425 }
427 
428 BEGIN_SORT_FUNCTION(TotalRateAverage)
429 {
430  sortResult = uint64cmp(diskItem1->ReadAverage + diskItem1->WriteAverage, diskItem2->ReadAverage + diskItem2->WriteAverage);
431 }
433 
435 {
436  sortResult = uintcmp(diskItem1->IoPriority, diskItem2->IoPriority);
437 }
439 
440 BEGIN_SORT_FUNCTION(ResponseTime)
441 {
442  sortResult = singlecmp(diskItem1->ResponseTimeAverage, diskItem2->ResponseTimeAverage);
443 }
445 
447  _In_ HWND hwnd,
448  _In_ PH_TREENEW_MESSAGE Message,
449  _In_opt_ PVOID Parameter1,
450  _In_opt_ PVOID Parameter2,
451  _In_opt_ PVOID Context
452  )
453 {
454  PET_DISK_NODE node;
455 
456  switch (Message)
457  {
458  case TreeNewGetChildren:
459  {
460  PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
461 
462  if (!getChildren->Node)
463  {
464  static PVOID sortFunctions[] =
465  {
466  SORT_FUNCTION(Process),
467  SORT_FUNCTION(File),
468  SORT_FUNCTION(ReadRateAverage),
469  SORT_FUNCTION(WriteRateAverage),
470  SORT_FUNCTION(TotalRateAverage),
471  SORT_FUNCTION(IoPriority),
472  SORT_FUNCTION(ResponseTime)
473  };
474  int (__cdecl *sortFunction)(const void *, const void *);
475 
476  if (DiskTreeNewSortColumn < ETDSTNC_MAXIMUM)
477  sortFunction = sortFunctions[DiskTreeNewSortColumn];
478  else
479  sortFunction = NULL;
480 
481  if (sortFunction)
482  {
483  qsort(DiskNodeList->Items, DiskNodeList->Count, sizeof(PVOID), sortFunction);
484  }
485 
486  getChildren->Children = (PPH_TREENEW_NODE *)DiskNodeList->Items;
487  getChildren->NumberOfChildren = DiskNodeList->Count;
488  }
489  }
490  return TRUE;
491  case TreeNewIsLeaf:
492  {
493  PPH_TREENEW_IS_LEAF isLeaf = Parameter1;
494 
495  isLeaf->IsLeaf = TRUE;
496  }
497  return TRUE;
498  case TreeNewGetCellText:
499  {
500  PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1;
501  PET_DISK_ITEM diskItem;
502 
503  node = (PET_DISK_NODE)getCellText->Node;
504  diskItem = node->DiskItem;
505 
506  switch (getCellText->Id)
507  {
508  case ETDSTNC_NAME:
509  getCellText->Text = node->ProcessNameText->sr;
510  break;
511  case ETDSTNC_FILE:
512  getCellText->Text = diskItem->FileNameWin32->sr;
513  break;
515  EtFormatRate(diskItem->ReadAverage, &node->ReadRateAverageText, &getCellText->Text);
516  break;
518  EtFormatRate(diskItem->WriteAverage, &node->WriteRateAverageText, &getCellText->Text);
519  break;
521  EtFormatRate(diskItem->ReadAverage + diskItem->WriteAverage, &node->TotalRateAverageText, &getCellText->Text);
522  break;
523  case ETDSTNC_IOPRIORITY:
524  switch (diskItem->IoPriority)
525  {
526  case IoPriorityVeryLow:
527  PhInitializeStringRef(&getCellText->Text, L"Very Low");
528  break;
529  case IoPriorityLow:
530  PhInitializeStringRef(&getCellText->Text, L"Low");
531  break;
532  case IoPriorityNormal:
533  PhInitializeStringRef(&getCellText->Text, L"Normal");
534  break;
535  case IoPriorityHigh:
536  PhInitializeStringRef(&getCellText->Text, L"High");
537  break;
538  case IoPriorityCritical:
539  PhInitializeStringRef(&getCellText->Text, L"Critical");
540  break;
541  default:
542  PhInitializeStringRef(&getCellText->Text, L"Unknown");
543  break;
544  }
545  break;
547  {
548  PH_FORMAT format;
549 
550  PhInitFormatF(&format, diskItem->ResponseTimeAverage, 0);
551  PhMoveReference(&node->ResponseTimeText, PhFormat(&format, 1, 0));
552  getCellText->Text = node->ResponseTimeText->sr;
553  }
554  break;
555  default:
556  return FALSE;
557  }
558 
559  getCellText->Flags = TN_CACHE;
560  }
561  return TRUE;
562  case TreeNewGetNodeIcon:
563  {
564  PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1;
565 
566  node = (PET_DISK_NODE)getNodeIcon->Node;
567 
568  if (node->DiskItem->ProcessIcon)
569  {
570  getNodeIcon->Icon = node->DiskItem->ProcessIcon->Icon;
571  }
572  else
573  {
574  PhGetStockApplicationIcon(&getNodeIcon->Icon, NULL);
575  }
576 
577  getNodeIcon->Flags = TN_CACHE;
578  }
579  return TRUE;
581  {
582  PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1;
583  PPH_PROCESS_NODE processNode;
584 
585  node = (PET_DISK_NODE)getCellTooltip->Node;
586 
587  if (getCellTooltip->Column->Id != 0)
588  return FALSE;
589 
590  if (!node->TooltipText)
591  {
592  if (processNode = PhFindProcessNode(node->DiskItem->ProcessId))
593  {
594  PPH_TREENEW_CALLBACK callback;
595  PVOID callbackContext;
596  PPH_TREENEW_COLUMN fixedColumn;
597  PH_TREENEW_GET_CELL_TOOLTIP fakeGetCellTooltip;
598 
599  // HACK: Get the tooltip text by using the treenew callback of the process tree.
600  if (TreeNew_GetCallback(ProcessTreeNewHandle, &callback, &callbackContext) &&
602  {
603  fakeGetCellTooltip.Flags = 0;
604  fakeGetCellTooltip.Node = &processNode->Node;
605  fakeGetCellTooltip.Column = fixedColumn;
606  fakeGetCellTooltip.Unfolding = FALSE;
607  PhInitializeEmptyStringRef(&fakeGetCellTooltip.Text);
608  fakeGetCellTooltip.Font = getCellTooltip->Font;
609  fakeGetCellTooltip.MaximumWidth = getCellTooltip->MaximumWidth;
610 
611  if (callback(ProcessTreeNewHandle, TreeNewGetCellTooltip, &fakeGetCellTooltip, NULL, callbackContext))
612  {
613  node->TooltipText = PhCreateString2(&fakeGetCellTooltip.Text);
614  }
615  }
616  }
617  }
618 
620  {
621  getCellTooltip->Text = node->TooltipText->sr;
622  getCellTooltip->Unfolding = FALSE;
623  getCellTooltip->MaximumWidth = -1;
624  }
625  else
626  {
627  return FALSE;
628  }
629  }
630  return TRUE;
631  case TreeNewSortChanged:
632  {
633  TreeNew_GetSort(hwnd, &DiskTreeNewSortColumn, &DiskTreeNewSortOrder);
634  // Force a rebuild to sort the items.
636  }
637  return TRUE;
638  case TreeNewKeyDown:
639  {
640  PPH_TREENEW_KEY_EVENT keyEvent = Parameter1;
641 
642  switch (keyEvent->VirtualKey)
643  {
644  case 'C':
645  if (GetKeyState(VK_CONTROL) < 0)
647  break;
648  case 'A':
649  if (GetKeyState(VK_CONTROL) < 0)
650  TreeNew_SelectRange(DiskTreeNewHandle, 0, -1);
651  break;
652  case VK_RETURN:
654  break;
655  }
656  }
657  return TRUE;
659  {
661 
662  data.TreeNewHandle = hwnd;
663  data.MouseEvent = Parameter1;
664  data.DefaultSortColumn = 0;
667 
672  }
673  return TRUE;
675  {
677  }
678  return TRUE;
679  case TreeNewContextMenu:
680  {
681  PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1;
682 
683  EtShowDiskContextMenu(mouseEvent->Location);
684  }
685  return TRUE;
686  case TreeNewDestroying:
687  {
689  }
690  return TRUE;
691  }
692 
693  return FALSE;
694 }
695 
697  _In_ PET_DISK_ITEM DiskItem
698  )
699 {
700  PH_FORMAT format[4];
701 
702  if (!DiskItem->ProcessId)
703  return PhCreateString(L"No Process");
704 
705  PhInitFormatS(&format[1], L" (");
706  PhInitFormatU(&format[2], HandleToUlong(DiskItem->ProcessId));
707  PhInitFormatC(&format[3], ')');
708 
709  if (DiskItem->ProcessName)
710  PhInitFormatSR(&format[0], DiskItem->ProcessName->sr);
711  else
712  PhInitFormatS(&format[0], L"Unknown Process");
713 
714  return PhFormat(format, 4, 96);
715 }
716 
718  VOID
719  )
720 {
721  PET_DISK_ITEM diskItem = NULL;
722  ULONG i;
723 
724  for (i = 0; i < DiskNodeList->Count; i++)
725  {
726  PET_DISK_NODE node = DiskNodeList->Items[i];
727 
728  if (node->Node.Selected)
729  {
730  diskItem = node->DiskItem;
731  break;
732  }
733  }
734 
735  return diskItem;
736 }
737 
739  _Out_ PET_DISK_ITEM **DiskItems,
740  _Out_ PULONG NumberOfDiskItems
741  )
742 {
743  PPH_LIST list;
744  ULONG i;
745 
746  list = PhCreateList(2);
747 
748  for (i = 0; i < DiskNodeList->Count; i++)
749  {
750  PET_DISK_NODE node = DiskNodeList->Items[i];
751 
752  if (node->Node.Selected)
753  {
754  PhAddItemList(list, node->DiskItem);
755  }
756  }
757 
758  *DiskItems = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
759  *NumberOfDiskItems = list->Count;
760 
761  PhDereferenceObject(list);
762 }
763 
765  VOID
766  )
767 {
768  TreeNew_DeselectRange(DiskTreeNewHandle, 0, -1);
769 }
770 
772  _In_ PET_DISK_NODE DiskNode
773  )
774 {
776 
777  if (!DiskNode->Node.Visible)
778  return;
779 
780  TreeNew_SetFocusNode(DiskTreeNewHandle, &DiskNode->Node);
781  TreeNew_SetMarkNode(DiskTreeNewHandle, &DiskNode->Node);
782  TreeNew_SelectRange(DiskTreeNewHandle, DiskNode->Node.Index, DiskNode->Node.Index);
783  TreeNew_EnsureVisible(DiskTreeNewHandle, &DiskNode->Node);
784 }
785 
787  VOID
788  )
789 {
790  PPH_STRING text;
791 
792  text = PhGetTreeNewText(DiskTreeNewHandle, 0);
793  PhSetClipboardString(DiskTreeNewHandle, &text->sr);
794  PhDereferenceObject(text);
795 }
796 
798  _Inout_ PPH_FILE_STREAM FileStream,
799  _In_ ULONG Mode
800  )
801 {
802  PPH_LIST lines;
803  ULONG i;
804 
805  lines = PhGetGenericTreeNewLines(DiskTreeNewHandle, Mode);
806 
807  for (i = 0; i < lines->Count; i++)
808  {
809  PPH_STRING line;
810 
811  line = lines->Items[i];
812  PhWriteStringAsUtf8FileStream(FileStream, &line->sr);
813  PhDereferenceObject(line);
814  PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n");
815  }
816 
817  PhDereferenceObject(lines);
818 }
819 
821  _In_ ULONG Id
822  )
823 {
824  switch (Id)
825  {
826  case ID_DISK_GOTOPROCESS:
827  {
829  PPH_PROCESS_NODE processNode;
830 
831  if (diskItem)
832  {
833  PhReferenceObject(diskItem);
834 
835  if (diskItem->ProcessRecord)
836  {
837  // Check if this is really the process that we want, or if it's just a case of PID re-use.
838  if ((processNode = PhFindProcessNode(diskItem->ProcessId)) &&
839  processNode->ProcessItem->CreateTime.QuadPart == diskItem->ProcessRecord->CreateTime.QuadPart)
840  {
843  }
844  else
845  {
847  }
848  }
849  else
850  {
851  PhShowError(PhMainWndHandle, L"The process does not exist.");
852  }
853 
854  PhDereferenceObject(diskItem);
855  }
856  }
857  break;
859  {
861 
862  if (diskItem)
863  {
865  }
866  }
867  break;
868  case ID_DISK_COPY:
869  {
870  EtCopyDiskList();
871  }
872  break;
873  case ID_DISK_PROPERTIES:
874  {
876 
877  if (diskItem)
878  {
880  }
881  }
882  break;
883  }
884 }
885 
887  _In_ PPH_EMENU Menu,
888  _In_ PET_DISK_ITEM *DiskItems,
889  _In_ ULONG NumberOfDiskItems
890  )
891 {
892  PPH_EMENU_ITEM item;
893 
894  if (NumberOfDiskItems == 0)
895  {
897  }
898  else if (NumberOfDiskItems == 1)
899  {
900  PPH_PROCESS_ITEM processItem;
901 
902  // If we have a process record and the process has terminated, we can only show
903  // process properties.
904  if (DiskItems[0]->ProcessRecord)
905  {
906  if (processItem = PhReferenceProcessItemForRecord(DiskItems[0]->ProcessRecord))
907  {
908  PhDereferenceObject(processItem);
909  }
910  else
911  {
912  if (item = PhFindEMenuItem(Menu, 0, NULL, ID_DISK_GOTOPROCESS))
913  {
914  item->Text = L"Process Properties";
915  item->Flags &= ~PH_EMENU_TEXT_OWNED;
916  }
917  }
918  }
919  }
920  else
921  {
924  }
925 }
926 
928  _In_ POINT Location
929  )
930 {
931  PET_DISK_ITEM *diskItems;
932  ULONG numberOfDiskItems;
933 
934  EtGetSelectedDiskItems(&diskItems, &numberOfDiskItems);
935 
936  if (numberOfDiskItems != 0)
937  {
938  PPH_EMENU menu;
939  PPH_EMENU_ITEM item;
940 
941  menu = PhCreateEMenu();
942  PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_DISK), 0);
944 
945  EtpInitializeDiskMenu(menu, diskItems, numberOfDiskItems);
946 
947  item = PhShowEMenu(
948  menu,
952  Location.x,
953  Location.y
954  );
955 
956  if (item)
957  {
958  EtHandleDiskCommand(item->Id);
959  }
960 
961  PhDestroyEMenu(menu);
962  }
963 
964  PhFree(diskItems);
965 }
966 
967 static VOID NTAPI EtpDiskItemAddedHandler(
968  _In_opt_ PVOID Parameter,
969  _In_opt_ PVOID Context
970  )
971 {
972  PET_DISK_ITEM diskItem = (PET_DISK_ITEM)Parameter;
973 
974  PhReferenceObject(diskItem);
976 }
977 
978 static VOID NTAPI EtpDiskItemModifiedHandler(
979  _In_opt_ PVOID Parameter,
980  _In_opt_ PVOID Context
981  )
982 {
984 }
985 
986 static VOID NTAPI EtpDiskItemRemovedHandler(
987  _In_opt_ PVOID Parameter,
988  _In_opt_ PVOID Context
989  )
990 {
992 }
993 
994 static VOID NTAPI EtpDiskItemsUpdatedHandler(
995  _In_opt_ PVOID Parameter,
996  _In_opt_ PVOID Context
997  )
998 {
1000 }
1001 
1002 static VOID NTAPI EtpOnDiskItemAdded(
1003  _In_ PVOID Parameter
1004  )
1005 {
1006  PET_DISK_ITEM diskItem = Parameter;
1007  PET_DISK_NODE diskNode;
1008 
1009  if (!DiskNeedsRedraw)
1010  {
1011  TreeNew_SetRedraw(DiskTreeNewHandle, FALSE);
1012  DiskNeedsRedraw = TRUE;
1013  }
1014 
1015  diskNode = EtAddDiskNode(diskItem);
1016  PhDereferenceObject(diskItem);
1017 }
1018 
1019 static VOID NTAPI EtpOnDiskItemModified(
1020  _In_ PVOID Parameter
1021  )
1022 {
1023  PET_DISK_ITEM diskItem = Parameter;
1024 
1025  EtUpdateDiskNode(EtFindDiskNode(diskItem));
1026 }
1027 
1028 static VOID NTAPI EtpOnDiskItemRemoved(
1029  _In_ PVOID Parameter
1030  )
1031 {
1032  PET_DISK_ITEM diskItem = Parameter;
1033 
1034  if (!DiskNeedsRedraw)
1035  {
1036  TreeNew_SetRedraw(DiskTreeNewHandle, FALSE);
1037  DiskNeedsRedraw = TRUE;
1038  }
1039 
1040  EtRemoveDiskNode(EtFindDiskNode(diskItem));
1041 }
1042 
1043 static VOID NTAPI EtpOnDiskItemsUpdated(
1044  _In_ PVOID Parameter
1045  )
1046 {
1047  ULONG i;
1048 
1049  if (DiskNeedsRedraw)
1050  {
1051  TreeNew_SetRedraw(DiskTreeNewHandle, TRUE);
1052  DiskNeedsRedraw = FALSE;
1053  }
1054 
1055  // Text invalidation
1056 
1057  for (i = 0; i < DiskNodeList->Count; i++)
1058  {
1059  PET_DISK_NODE node = DiskNodeList->Items[i];
1060 
1061  // The name and file name never change, so we don't invalidate that.
1062  memset(&node->TextCache[2], 0, sizeof(PH_STRINGREF) * (ETDSTNC_MAXIMUM - 2));
1063  // Always get the newest tooltip text from the process tree.
1064  PhClearReference(&node->TooltipText);
1065  }
1066 
1067  InvalidateRect(DiskTreeNewHandle, NULL, FALSE);
1068 }
1069 
1071  _In_opt_ PVOID Parameter,
1072  _In_opt_ PVOID Context
1073  )
1074 {
1075  if (!EtEtwEnabled)
1076  return;
1077 
1078  PhApplyTreeNewFilters(&FilterSupport);
1079 }
1080 
1082  _In_ PPH_TREENEW_NODE Node,
1083  _In_opt_ PVOID Context
1084  )
1085 {
1086  PET_DISK_NODE diskNode = (PET_DISK_NODE)Node;
1087  PTOOLSTATUS_WORD_MATCH wordMatch = ToolStatusInterface->WordMatch;
1088 
1089  if (PhIsNullOrEmptyString(ToolStatusInterface->GetSearchboxText()))
1090  return TRUE;
1091 
1092  if (wordMatch(&diskNode->ProcessNameText->sr))
1093  return TRUE;
1094 
1095  if (wordMatch(&diskNode->DiskItem->FileNameWin32->sr))
1096  return TRUE;
1097 
1098  return FALSE;
1099 }
1100 
1102  _In_ BOOLEAN Select
1103  )
1104 {
1105  SetFocus(DiskTreeNewHandle);
1106 
1107  if (Select)
1108  {
1109  if (TreeNew_GetFlatNodeCount(DiskTreeNewHandle) > 0)
1111  }
1112 }
1113 
1115  VOID
1116  )
1117 {
1118  return DiskTreeNewHandle;
1119 }
1120 
1121 INT_PTR CALLBACK EtpDiskTabErrorDialogProc(
1122  _In_ HWND hwndDlg,
1123  _In_ UINT uMsg,
1124  _In_ WPARAM wParam,
1125  _In_ LPARAM lParam
1126  )
1127 {
1128  switch (uMsg)
1129  {
1130  case WM_INITDIALOG:
1131  {
1132  if (!PhElevated)
1133  {
1134  SendMessage(GetDlgItem(hwndDlg, IDC_RESTART), BCM_SETSHIELD, 0, TRUE);
1135  }
1136  else
1137  {
1138  SetDlgItemText(hwndDlg, IDC_ERROR, L"Unable to start the kernel event tracing session.");
1139  ShowWindow(GetDlgItem(hwndDlg, IDC_RESTART), SW_HIDE);
1140  }
1141  }
1142  break;
1143  case WM_COMMAND:
1144  {
1145  switch (LOWORD(wParam))
1146  {
1147  case IDC_RESTART:
1149 
1152  L"-v -selecttab Disk",
1153  SW_SHOW,
1156  0,
1157  NULL
1158  ))
1159  {
1161  }
1162  else
1163  {
1165  }
1166 
1167  break;
1168  }
1169  }
1170  break;
1171  case WM_CTLCOLORBTN:
1172  case WM_CTLCOLORSTATIC:
1173  {
1174  SetBkMode((HDC)wParam, TRANSPARENT);
1175  return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
1176  }
1177  break;
1178  }
1179 
1180  return FALSE;
1181 }