Process Hacker
thrdlist.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * thread list
4  *
5  * Copyright (C) 2011-2012 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 <extmgri.h>
26 #include <phplug.h>
27 #include <emenu.h>
28 #include <procprpp.h>
29 
31  _In_ PVOID Entry1,
32  _In_ PVOID Entry2
33  );
34 
36  _In_ PVOID Entry
37  );
38 
40  _In_ PPH_THREAD_NODE ThreadNode
41  );
42 
44  _In_ PPH_THREAD_NODE ThreadNode,
45  _In_ PPH_THREAD_LIST_CONTEXT Context
46  );
47 
49  _In_ LONG Result,
50  _In_ PVOID Node1,
51  _In_ PVOID Node2,
52  _In_ PH_SORT_ORDER SortOrder
53  );
54 
55 BOOLEAN NTAPI PhpThreadTreeNewCallback(
56  _In_ HWND hwnd,
57  _In_ PH_TREENEW_MESSAGE Message,
58  _In_opt_ PVOID Parameter1,
59  _In_opt_ PVOID Parameter2,
60  _In_opt_ PVOID Context
61  );
62 
64  _In_ HWND ParentWindowHandle,
65  _In_ HWND TreeNewHandle,
66  _Out_ PPH_THREAD_LIST_CONTEXT Context
67  )
68 {
69  HWND hwnd;
70 
71  memset(Context, 0, sizeof(PH_THREAD_LIST_CONTEXT));
72  Context->EnableStateHighlighting = TRUE;
73 
74  Context->NodeHashtable = PhCreateHashtable(
75  sizeof(PPH_THREAD_NODE),
78  100
79  );
80  Context->NodeList = PhCreateList(100);
81 
82  Context->ParentWindowHandle = ParentWindowHandle;
83  Context->TreeNewHandle = TreeNewHandle;
84  hwnd = TreeNewHandle;
85  PhSetControlTheme(hwnd, L"explorer");
86 
88 
89  TreeNew_SetRedraw(hwnd, FALSE);
90 
91  // Default columns
92  PhAddTreeNewColumn(hwnd, PHTHTLC_TID, TRUE, L"TID", 50, PH_ALIGN_LEFT, 0, 0);
93  PhAddTreeNewColumnEx(hwnd, PHTHTLC_CPU, TRUE, L"CPU", 45, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE);
94  PhAddTreeNewColumnEx(hwnd, PHTHTLC_CYCLESDELTA, TRUE, L"Cycles Delta", 80, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE);
95  PhAddTreeNewColumn(hwnd, PHTHTLC_STARTADDRESS, TRUE, L"Start Address", 180, PH_ALIGN_LEFT, 3, 0);
96  PhAddTreeNewColumnEx(hwnd, PHTHTLC_PRIORITY, TRUE, L"Priority", 80, PH_ALIGN_LEFT, 4, 0, TRUE);
97  PhAddTreeNewColumn(hwnd, PHTHTLC_SERVICE, FALSE, L"Service", 100, PH_ALIGN_LEFT, -1, 0);
98 
99  TreeNew_SetRedraw(hwnd, TRUE);
100 
102 
104 }
105 
107  _In_ PPH_THREAD_LIST_CONTEXT Context
108  )
109 {
110  ULONG i;
111 
112  PhCmDeleteManager(&Context->Cm);
113 
114  for (i = 0; i < Context->NodeList->Count; i++)
115  PhpDestroyThreadNode(Context->NodeList->Items[i]);
116 
117  PhDereferenceObject(Context->NodeHashtable);
118  PhDereferenceObject(Context->NodeList);
119 }
120 
122  _In_ PVOID Entry1,
123  _In_ PVOID Entry2
124  )
125 {
126  PPH_THREAD_NODE threadNode1 = *(PPH_THREAD_NODE *)Entry1;
127  PPH_THREAD_NODE threadNode2 = *(PPH_THREAD_NODE *)Entry2;
128 
129  return threadNode1->ThreadId == threadNode2->ThreadId;
130 }
131 
133  _In_ PVOID Entry
134  )
135 {
136  return (ULONG)(*(PPH_THREAD_NODE *)Entry)->ThreadId / 4;
137 }
138 
140  _Inout_ PPH_THREAD_LIST_CONTEXT Context
141  )
142 {
143  PPH_STRING settings;
144  PPH_STRING sortSettings;
145  PH_TREENEW_COLUMN column;
146  ULONG sortColumn;
147  PH_SORT_ORDER sortOrder;
148 
149  if (!Context->UseCycleTime)
150  {
151  column.Id = PHTHTLC_CYCLESDELTA;
152  column.Text = L"Context Switches Delta";
153  TreeNew_SetColumn(Context->TreeNewHandle, TN_COLUMN_TEXT, &column);
154  }
155 
156  if (Context->HasServices)
157  {
158  column.Id = PHTHTLC_SERVICE;
159  column.Visible = TRUE;
160  TreeNew_SetColumn(Context->TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &column);
161  }
162 
163  settings = PhGetStringSetting(L"ThreadTreeListColumns");
164  sortSettings = PhGetStringSetting(L"ThreadTreeListSort");
165  PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, PH_CM_COLUMN_WIDTHS_ONLY, &settings->sr, &sortSettings->sr);
166  PhDereferenceObject(settings);
167  PhDereferenceObject(sortSettings);
168 
169  TreeNew_GetSort(Context->TreeNewHandle, &sortColumn, &sortOrder);
170 
171  // Make sure we're not sorting by an invisible column.
172  if (sortOrder != NoSortOrder && !(TreeNew_GetColumn(Context->TreeNewHandle, sortColumn, &column) && column.Visible))
173  {
174  TreeNew_SetSort(Context->TreeNewHandle, PHTHTLC_CYCLESDELTA, DescendingSortOrder);
175  }
176 }
177 
179  _Inout_ PPH_THREAD_LIST_CONTEXT Context
180  )
181 {
182  PPH_STRING settings;
183  PPH_STRING sortSettings;
184 
185  settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, PH_CM_COLUMN_WIDTHS_ONLY, &sortSettings);
186  PhSetStringSetting2(L"ThreadTreeListColumns", &settings->sr);
187  PhSetStringSetting2(L"ThreadTreeListSort", &sortSettings->sr);
188  PhDereferenceObject(settings);
189  PhDereferenceObject(sortSettings);
190 }
191 
193  _Inout_ PPH_THREAD_LIST_CONTEXT Context,
194  _In_ PPH_THREAD_ITEM ThreadItem,
195  _In_ BOOLEAN FirstRun
196  )
197 {
198  PPH_THREAD_NODE threadNode;
199 
200  threadNode = PhAllocate(PhEmGetObjectSize(EmThreadNodeType, sizeof(PH_THREAD_NODE)));
201  memset(threadNode, 0, sizeof(PH_THREAD_NODE));
202  PhInitializeTreeNewNode(&threadNode->Node);
203 
204  if (Context->EnableStateHighlighting && !FirstRun)
205  {
207  &threadNode->Node,
208  &threadNode->ShState,
209  &Context->NodeStateList,
210  NewItemState,
211  PhCsColorNew,
212  NULL
213  );
214  }
215 
216  threadNode->ThreadId = ThreadItem->ThreadId;
217  threadNode->ThreadItem = ThreadItem;
218  PhReferenceObject(ThreadItem);
219 
220  memset(threadNode->TextCache, 0, sizeof(PH_STRINGREF) * PHTHTLC_MAXIMUM);
221  threadNode->Node.TextCache = threadNode->TextCache;
222  threadNode->Node.TextCacheSize = PHTHTLC_MAXIMUM;
223 
224  PhAddEntryHashtable(Context->NodeHashtable, &threadNode);
225  PhAddItemList(Context->NodeList, threadNode);
226 
228 
229  TreeNew_NodesStructured(Context->TreeNewHandle);
230 
231  return threadNode;
232 }
233 
235  _In_ PPH_THREAD_LIST_CONTEXT Context,
236  _In_ HANDLE ThreadId
237  )
238 {
239  PH_THREAD_NODE lookupThreadNode;
240  PPH_THREAD_NODE lookupThreadNodePtr = &lookupThreadNode;
241  PPH_THREAD_NODE *threadNode;
242 
243  lookupThreadNode.ThreadId = ThreadId;
244 
245  threadNode = (PPH_THREAD_NODE *)PhFindEntryHashtable(
246  Context->NodeHashtable,
247  &lookupThreadNodePtr
248  );
249 
250  if (threadNode)
251  return *threadNode;
252  else
253  return NULL;
254 }
255 
257  _In_ PPH_THREAD_LIST_CONTEXT Context,
258  _In_ PPH_THREAD_NODE ThreadNode
259  )
260 {
261  // Remove from the hashtable here to avoid problems in case the key is re-used.
262  PhRemoveEntryHashtable(Context->NodeHashtable, &ThreadNode);
263 
264  if (Context->EnableStateHighlighting)
265  {
267  &ThreadNode->Node,
268  &ThreadNode->ShState,
269  &Context->NodeStateList,
272  Context->TreeNewHandle
273  );
274  }
275  else
276  {
277  PhpRemoveThreadNode(ThreadNode, Context);
278  }
279 }
280 
282  _In_ PPH_THREAD_NODE ThreadNode
283  )
284 {
286 
287  if (ThreadNode->CyclesDeltaText) PhDereferenceObject(ThreadNode->CyclesDeltaText);
288  if (ThreadNode->StartAddressText) PhDereferenceObject(ThreadNode->StartAddressText);
289  if (ThreadNode->PriorityText) PhDereferenceObject(ThreadNode->PriorityText);
290 
291  PhDereferenceObject(ThreadNode->ThreadItem);
292 
293  PhFree(ThreadNode);
294 }
295 
297  _In_ PPH_THREAD_NODE ThreadNode,
298  _In_ PPH_THREAD_LIST_CONTEXT Context // PH_TICK_SH_STATE requires this parameter to be after ThreadNode
299  )
300 {
301  ULONG index;
302 
303  // Remove from list and cleanup.
304 
305  if ((index = PhFindItemList(Context->NodeList, ThreadNode)) != -1)
306  PhRemoveItemList(Context->NodeList, index);
307 
308  PhpDestroyThreadNode(ThreadNode);
309 
310  TreeNew_NodesStructured(Context->TreeNewHandle);
311 }
312 
314  _In_ PPH_THREAD_LIST_CONTEXT Context,
315  _In_ PPH_THREAD_NODE ThreadNode
316  )
317 {
318  memset(ThreadNode->TextCache, 0, sizeof(PH_STRINGREF) * PHTHTLC_MAXIMUM);
319 
320  ThreadNode->ValidMask = 0;
321  PhInvalidateTreeNewNode(&ThreadNode->Node, TN_CACHE_COLOR);
322  TreeNew_NodesStructured(Context->TreeNewHandle);
323 }
324 
326  _In_ PPH_THREAD_LIST_CONTEXT Context
327  )
328 {
329  PH_TICK_SH_STATE_TN(PH_THREAD_NODE, ShState, Context->NodeStateList, PhpRemoveThreadNode, PhCsHighlightingDuration, Context->TreeNewHandle, TRUE, NULL, Context);
330 }
331 
332 #define SORT_FUNCTION(Column) PhpThreadTreeNewCompare##Column
333 
334 #define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpThreadTreeNewCompare##Column( \
335  _In_ void *_context, \
336  _In_ const void *_elem1, \
337  _In_ const void *_elem2 \
338  ) \
339 { \
340  PPH_THREAD_NODE node1 = *(PPH_THREAD_NODE *)_elem1; \
341  PPH_THREAD_NODE node2 = *(PPH_THREAD_NODE *)_elem2; \
342  PPH_THREAD_ITEM threadItem1 = node1->ThreadItem; \
343  PPH_THREAD_ITEM threadItem2 = node2->ThreadItem; \
344  PPH_THREAD_LIST_CONTEXT context = (PPH_THREAD_LIST_CONTEXT)_context; \
345  int sortResult = 0;
346 
347 #define END_SORT_FUNCTION \
348  if (sortResult == 0) \
349  sortResult = uintptrcmp((ULONG_PTR)node1->ThreadId, (ULONG_PTR)node2->ThreadId); \
350  \
351  return PhModifySort(sortResult, context->TreeNewSortOrder); \
352 }
353 
355  _In_ LONG Result,
356  _In_ PVOID Node1,
357  _In_ PVOID Node2,
358  _In_ PH_SORT_ORDER SortOrder
359  )
360 {
361  if (Result == 0)
362  Result = uintptrcmp((ULONG_PTR)((PPH_THREAD_NODE)Node1)->ThreadId, (ULONG_PTR)((PPH_THREAD_NODE)Node2)->ThreadId);
363 
364  return PhModifySort(Result, SortOrder);
365 }
366 
368 {
369  sortResult = uintptrcmp((ULONG_PTR)node1->ThreadId, (ULONG_PTR)node2->ThreadId);
370 }
372 
374 {
375  sortResult = singlecmp(threadItem1->CpuUsage, threadItem2->CpuUsage);
376 
377  if (sortResult == 0)
378  {
379  if (context->UseCycleTime)
380  sortResult = uint64cmp(threadItem1->CyclesDelta.Delta, threadItem2->CyclesDelta.Delta);
381  else
382  sortResult = uintcmp(threadItem1->ContextSwitchesDelta.Delta, threadItem2->ContextSwitchesDelta.Delta);
383  }
384 }
386 
388 {
389  if (context->UseCycleTime)
390  sortResult = uint64cmp(threadItem1->CyclesDelta.Delta, threadItem2->CyclesDelta.Delta);
391  else
392  sortResult = uintcmp(threadItem1->ContextSwitchesDelta.Delta, threadItem2->ContextSwitchesDelta.Delta);
393 }
395 
396 BEGIN_SORT_FUNCTION(StartAddress)
397 {
398  sortResult = PhCompareStringWithNull(threadItem1->StartAddressString, threadItem2->StartAddressString, TRUE);
399 }
401 
403 {
404  sortResult = intcmp(threadItem1->PriorityWin32, threadItem2->PriorityWin32);
405 }
407 
409 {
410  sortResult = PhCompareStringWithNull(threadItem1->ServiceName, threadItem2->ServiceName, TRUE);
411 }
413 
415  _In_ HWND hwnd,
416  _In_ PH_TREENEW_MESSAGE Message,
417  _In_opt_ PVOID Parameter1,
418  _In_opt_ PVOID Parameter2,
419  _In_opt_ PVOID Context
420  )
421 {
422  PPH_THREAD_LIST_CONTEXT context;
423  PPH_THREAD_NODE node;
424 
425  context = Context;
426 
427  if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm))
428  return TRUE;
429 
430  switch (Message)
431  {
432  case TreeNewGetChildren:
433  {
434  PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
435 
436  if (!getChildren->Node)
437  {
438  static PVOID sortFunctions[] =
439  {
440  SORT_FUNCTION(Tid),
441  SORT_FUNCTION(Cpu),
442  SORT_FUNCTION(CyclesDelta),
443  SORT_FUNCTION(StartAddress),
444  SORT_FUNCTION(Priority),
445  SORT_FUNCTION(Service)
446  };
447  int (__cdecl *sortFunction)(void *, const void *, const void *);
448 
449  if (!PhCmForwardSort(
450  (PPH_TREENEW_NODE *)context->NodeList->Items,
451  context->NodeList->Count,
452  context->TreeNewSortColumn,
453  context->TreeNewSortOrder,
454  &context->Cm
455  ))
456  {
457  if (context->TreeNewSortColumn < PHTHTLC_MAXIMUM)
458  sortFunction = sortFunctions[context->TreeNewSortColumn];
459  else
460  sortFunction = NULL;
461  }
462  else
463  {
464  sortFunction = NULL;
465  }
466 
467  if (sortFunction)
468  {
469  qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context);
470  }
471 
472  getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items;
473  getChildren->NumberOfChildren = context->NodeList->Count;
474  }
475  }
476  return TRUE;
477  case TreeNewIsLeaf:
478  {
479  PPH_TREENEW_IS_LEAF isLeaf = Parameter1;
480 
481  isLeaf->IsLeaf = TRUE;
482  }
483  return TRUE;
484  case TreeNewGetCellText:
485  {
486  PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1;
487  PPH_THREAD_ITEM threadItem;
488 
489  node = (PPH_THREAD_NODE)getCellText->Node;
490  threadItem = node->ThreadItem;
491 
492  switch (getCellText->Id)
493  {
494  case PHTHTLC_TID:
495  PhInitializeStringRefLongHint(&getCellText->Text, threadItem->ThreadIdString);
496  break;
497  case PHTHTLC_CPU:
498  {
499  FLOAT cpuUsage;
500 
501  cpuUsage = threadItem->CpuUsage * 100;
502 
503  if (cpuUsage >= 0.01)
504  {
505  PH_FORMAT format;
506  SIZE_T returnLength;
507 
508  PhInitFormatF(&format, cpuUsage, 2);
509 
510  if (PhFormatToBuffer(&format, 1, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength))
511  {
512  getCellText->Text.Buffer = node->CpuUsageText;
513  getCellText->Text.Length = returnLength - sizeof(WCHAR); // minus null terminator
514  }
515  }
516  else if (cpuUsage != 0 && PhCsShowCpuBelow001)
517  {
518  PH_FORMAT format[2];
519  SIZE_T returnLength;
520 
521  PhInitFormatS(&format[0], L"< ");
522  PhInitFormatF(&format[1], 0.01, 2);
523 
524  if (PhFormatToBuffer(format, 2, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength))
525  {
526  getCellText->Text.Buffer = node->CpuUsageText;
527  getCellText->Text.Length = returnLength - sizeof(WCHAR);
528  }
529  }
530  }
531  break;
532  case PHTHTLC_CYCLESDELTA:
533  if (context->UseCycleTime)
534  {
535  if (threadItem->CyclesDelta.Delta != threadItem->CyclesDelta.Value && threadItem->CyclesDelta.Delta != 0)
536  {
538  getCellText->Text = node->CyclesDeltaText->sr;
539  }
540  }
541  else
542  {
543  if (threadItem->ContextSwitchesDelta.Delta != threadItem->ContextSwitchesDelta.Value && threadItem->ContextSwitchesDelta.Delta != 0)
544  {
546  getCellText->Text = node->CyclesDeltaText->sr;
547  }
548  }
549  break;
552  getCellText->Text = PhGetStringRef(node->StartAddressText);
553  break;
554  case PHTHTLC_PRIORITY:
556  getCellText->Text = PhGetStringRef(node->PriorityText);
557  break;
558  case PHTHTLC_SERVICE:
559  getCellText->Text = PhGetStringRef(threadItem->ServiceName);
560  break;
561  default:
562  return FALSE;
563  }
564 
565  getCellText->Flags = TN_CACHE;
566  }
567  return TRUE;
568  case TreeNewGetNodeColor:
569  {
570  PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1;
571  PPH_THREAD_ITEM threadItem;
572 
573  node = (PPH_THREAD_NODE)getNodeColor->Node;
574  threadItem = node->ThreadItem;
575 
576  if (!threadItem)
577  ; // Dummy
578  else if (PhCsUseColorSuspended && threadItem->WaitReason == Suspended)
579  getNodeColor->BackColor = PhCsColorSuspended;
580  else if (PhCsUseColorGuiThreads && threadItem->IsGuiThread)
581  getNodeColor->BackColor = PhCsColorGuiThreads;
582 
583  getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR;
584  }
585  return TRUE;
586  case TreeNewSortChanged:
587  {
588  TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder);
589  // Force a rebuild to sort the items.
591  }
592  return TRUE;
594  {
595  SendMessage(context->ParentWindowHandle, WM_PH_THREAD_SELECTION_CHANGED, 0, 0);
596  }
597  return TRUE;
598  case TreeNewKeyDown:
599  {
600  PPH_TREENEW_KEY_EVENT keyEvent = Parameter1;
601 
602  switch (keyEvent->VirtualKey)
603  {
604  case 'C':
605  if (GetKeyState(VK_CONTROL) < 0)
606  SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_COPY, 0);
607  break;
608  case 'A':
609  if (GetKeyState(VK_CONTROL) < 0)
610  TreeNew_SelectRange(context->TreeNewHandle, 0, -1);
611  break;
612  case VK_DELETE:
613  SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_TERMINATE, 0);
614  break;
615  case VK_RETURN:
616  SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_INSPECT, 0);
617  break;
618  }
619  }
620  return TRUE;
622  {
624 
625  // Customizable columns are disabled until we can figure out how to make it
626  // co-operate with the column adjustments (e.g. Cycles Delta vs Context Switches Delta,
627  // Service column).
628 
629  data.TreeNewHandle = hwnd;
630  data.MouseEvent = Parameter1;
634 
639  }
640  return TRUE;
642  {
643  SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_INSPECT, 0);
644  }
645  return TRUE;
646  case TreeNewContextMenu:
647  {
648  PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1;
649 
650  SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu);
651  }
652  return TRUE;
654  {
655  PULONG code = Parameter2;
656 
657  if (PtrToUlong(Parameter1) == VK_RETURN)
658  {
659  *code = DLGC_WANTMESSAGE;
660  return TRUE;
661  }
662  }
663  return FALSE;
664  }
665 
666  return FALSE;
667 }
668 
670  _In_ PPH_THREAD_LIST_CONTEXT Context
671  )
672 {
673  PPH_THREAD_ITEM threadItem = NULL;
674  ULONG i;
675 
676  for (i = 0; i < Context->NodeList->Count; i++)
677  {
678  PPH_THREAD_NODE node = Context->NodeList->Items[i];
679 
680  if (node->Node.Selected)
681  {
682  threadItem = node->ThreadItem;
683  break;
684  }
685  }
686 
687  return threadItem;
688 }
689 
691  _In_ PPH_THREAD_LIST_CONTEXT Context,
692  _Out_ PPH_THREAD_ITEM **Threads,
693  _Out_ PULONG NumberOfThreads
694  )
695 {
696  PPH_LIST list;
697  ULONG i;
698 
699  list = PhCreateList(2);
700 
701  for (i = 0; i < Context->NodeList->Count; i++)
702  {
703  PPH_THREAD_NODE node = Context->NodeList->Items[i];
704 
705  if (node->Node.Selected)
706  {
707  PhAddItemList(list, node->ThreadItem);
708  }
709  }
710 
711  *Threads = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
712  *NumberOfThreads = list->Count;
713 
714  PhDereferenceObject(list);
715 }
716 
718  _In_ PPH_THREAD_LIST_CONTEXT Context
719  )
720 {
721  TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1);
722 }