Process Hacker
wndtree.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Window Explorer -
3  * window treelist
4  *
5  * Copyright (C) 2011 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 "wndexp.h"
24 #include "resource.h"
25 
27  _In_ PVOID Entry1,
28  _In_ PVOID Entry2
29  );
30 
32  _In_ PVOID Entry
33  );
34 
36  _In_ PWE_WINDOW_NODE WindowNode
37  );
38 
39 BOOLEAN NTAPI WepWindowTreeNewCallback(
40  _In_ HWND hwnd,
41  _In_ PH_TREENEW_MESSAGE Message,
42  _In_opt_ PVOID Parameter1,
43  _In_opt_ PVOID Parameter2,
44  _In_opt_ PVOID Context
45  );
46 
48  _In_ HWND ParentWindowHandle,
49  _In_ HWND TreeNewHandle,
50  _Out_ PWE_WINDOW_TREE_CONTEXT Context
51  )
52 {
53  HWND hwnd;
54  PPH_STRING settings;
55 
56  memset(Context, 0, sizeof(WE_WINDOW_TREE_CONTEXT));
57 
58  Context->NodeHashtable = PhCreateHashtable(
59  sizeof(PWE_WINDOW_NODE),
62  100
63  );
64  Context->NodeList = PhCreateList(100);
65  Context->NodeRootList = PhCreateList(30);
66 
67  Context->ParentWindowHandle = ParentWindowHandle;
68  Context->TreeNewHandle = TreeNewHandle;
69  hwnd = TreeNewHandle;
70  PhSetControlTheme(hwnd, L"explorer");
71 
73 
74  PhAddTreeNewColumn(hwnd, WEWNTLC_CLASS, TRUE, L"Class", 180, PH_ALIGN_LEFT, 0, 0);
75  PhAddTreeNewColumn(hwnd, WEWNTLC_HANDLE, TRUE, L"Handle", 70, PH_ALIGN_LEFT, 1, 0);
76  PhAddTreeNewColumn(hwnd, WEWNTLC_TEXT, TRUE, L"Text", 220, PH_ALIGN_LEFT, 2, 0);
77  PhAddTreeNewColumn(hwnd, WEWNTLC_THREAD, TRUE, L"Thread", 150, PH_ALIGN_LEFT, 3, 0);
78 
80  TreeNew_SetSort(hwnd, 0, NoSortOrder);
81 
83  PhCmLoadSettings(hwnd, &settings->sr);
84  PhDereferenceObject(settings);
85 }
86 
88  _In_ PWE_WINDOW_TREE_CONTEXT Context
89  )
90 {
91  PPH_STRING settings;
92  ULONG i;
93 
94  settings = PhCmSaveSettings(Context->TreeNewHandle);
96  PhDereferenceObject(settings);
97 
98  for (i = 0; i < Context->NodeList->Count; i++)
99  WepDestroyWindowNode(Context->NodeList->Items[i]);
100 
101  PhDereferenceObject(Context->NodeHashtable);
102  PhDereferenceObject(Context->NodeList);
103  PhDereferenceObject(Context->NodeRootList);
104 }
105 
107  _In_ PVOID Entry1,
108  _In_ PVOID Entry2
109  )
110 {
111  PWE_WINDOW_NODE windowNode1 = *(PWE_WINDOW_NODE *)Entry1;
112  PWE_WINDOW_NODE windowNode2 = *(PWE_WINDOW_NODE *)Entry2;
113 
114  return windowNode1->WindowHandle == windowNode2->WindowHandle;
115 }
116 
118  _In_ PVOID Entry
119  )
120 {
121  return PhHashIntPtr((ULONG_PTR)(*(PWE_WINDOW_NODE *)Entry)->WindowHandle);
122 }
123 
125  _Inout_ PWE_WINDOW_TREE_CONTEXT Context
126  )
127 {
128  PWE_WINDOW_NODE windowNode;
129 
130  windowNode = PhAllocate(sizeof(WE_WINDOW_NODE));
131  memset(windowNode, 0, sizeof(WE_WINDOW_NODE));
132  PhInitializeTreeNewNode(&windowNode->Node);
133 
134  memset(windowNode->TextCache, 0, sizeof(PH_STRINGREF) * WEWNTLC_MAXIMUM);
135  windowNode->Node.TextCache = windowNode->TextCache;
136  windowNode->Node.TextCacheSize = WEWNTLC_MAXIMUM;
137 
138  windowNode->Children = PhCreateList(1);
139 
140  PhAddEntryHashtable(Context->NodeHashtable, &windowNode);
141  PhAddItemList(Context->NodeList, windowNode);
142 
143  TreeNew_NodesStructured(Context->TreeNewHandle);
144 
145  return windowNode;
146 }
147 
149  _In_ PWE_WINDOW_TREE_CONTEXT Context,
150  _In_ HWND WindowHandle
151  )
152 {
153  WE_WINDOW_NODE lookupWindowNode;
154  PWE_WINDOW_NODE lookupWindowNodePtr = &lookupWindowNode;
155  PWE_WINDOW_NODE *windowNode;
156 
157  lookupWindowNode.WindowHandle = WindowHandle;
158 
159  windowNode = (PWE_WINDOW_NODE *)PhFindEntryHashtable(
160  Context->NodeHashtable,
161  &lookupWindowNodePtr
162  );
163 
164  if (windowNode)
165  return *windowNode;
166  else
167  return NULL;
168 }
169 
171  _In_ PWE_WINDOW_TREE_CONTEXT Context,
172  _In_ PWE_WINDOW_NODE WindowNode
173  )
174 {
175  ULONG index;
176 
177  // Remove from hashtable/list and cleanup.
178 
179  PhRemoveEntryHashtable(Context->NodeHashtable, &WindowNode);
180 
181  if ((index = PhFindItemList(Context->NodeList, WindowNode)) != -1)
182  PhRemoveItemList(Context->NodeList, index);
183 
184  WepDestroyWindowNode(WindowNode);
185 
186  TreeNew_NodesStructured(Context->TreeNewHandle);
187 }
188 
190  _In_ PWE_WINDOW_NODE WindowNode
191  )
192 {
193  PhDereferenceObject(WindowNode->Children);
194 
195  if (WindowNode->WindowText) PhDereferenceObject(WindowNode->WindowText);
196 
197  if (WindowNode->ThreadString) PhDereferenceObject(WindowNode->ThreadString);
198 
199  PhFree(WindowNode);
200 }
201 
202 #define SORT_FUNCTION(Column) WepWindowTreeNewCompare##Column
203 
204 #define BEGIN_SORT_FUNCTION(Column) static int __cdecl WepWindowTreeNewCompare##Column( \
205  _In_ void *_context, \
206  _In_ const void *_elem1, \
207  _In_ const void *_elem2 \
208  ) \
209 { \
210  PWE_WINDOW_NODE node1 = *(PWE_WINDOW_NODE *)_elem1; \
211  PWE_WINDOW_NODE node2 = *(PWE_WINDOW_NODE *)_elem2; \
212  int sortResult = 0;
213 
214 #define END_SORT_FUNCTION \
215  return PhModifySort(sortResult, ((PWE_WINDOW_TREE_CONTEXT)_context)->TreeNewSortOrder); \
216 }
217 
219 {
220  sortResult = wcsicmp(node1->WindowClass, node2->WindowClass);
221 }
223 
225 {
226  sortResult = uintptrcmp((ULONG_PTR)node1->WindowHandle, (ULONG_PTR)node2->WindowHandle);
227 }
229 
231 {
232  sortResult = PhCompareString(node1->WindowText, node2->WindowText, TRUE);
233 }
235 
237 {
238  sortResult = uintptrcmp((ULONG_PTR)node1->ClientId.UniqueProcess, (ULONG_PTR)node2->ClientId.UniqueProcess);
239 
240  if (sortResult == 0)
241  sortResult = uintptrcmp((ULONG_PTR)node1->ClientId.UniqueThread, (ULONG_PTR)node2->ClientId.UniqueThread);
242 }
244 
246  _In_ HWND hwnd,
247  _In_ PH_TREENEW_MESSAGE Message,
248  _In_opt_ PVOID Parameter1,
249  _In_opt_ PVOID Parameter2,
250  _In_opt_ PVOID Context
251  )
252 {
253  PWE_WINDOW_TREE_CONTEXT context;
254  PWE_WINDOW_NODE node;
255 
256  context = Context;
257 
258  switch (Message)
259  {
260  case TreeNewGetChildren:
261  {
262  PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
263 
264  node = (PWE_WINDOW_NODE)getChildren->Node;
265 
266  if (context->TreeNewSortOrder == NoSortOrder)
267  {
268  if (!node)
269  {
270  getChildren->Children = (PPH_TREENEW_NODE *)context->NodeRootList->Items;
271  getChildren->NumberOfChildren = context->NodeRootList->Count;
272  }
273  else
274  {
275  getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items;
276  getChildren->NumberOfChildren = node->Children->Count;
277  }
278  }
279  else
280  {
281  if (!node)
282  {
283  static PVOID sortFunctions[] =
284  {
285  SORT_FUNCTION(Class),
286  SORT_FUNCTION(Handle),
287  SORT_FUNCTION(Text),
288  SORT_FUNCTION(Thread)
289  };
290  int (__cdecl *sortFunction)(void *, const void *, const void *);
291 
292  if (context->TreeNewSortColumn < WEWNTLC_MAXIMUM)
293  sortFunction = sortFunctions[context->TreeNewSortColumn];
294  else
295  sortFunction = NULL;
296 
297  if (sortFunction)
298  {
299  qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context);
300  }
301 
302  getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items;
303  getChildren->NumberOfChildren = context->NodeList->Count;
304  }
305  }
306  }
307  return TRUE;
308  case TreeNewIsLeaf:
309  {
310  PPH_TREENEW_IS_LEAF isLeaf = Parameter1;
311 
312  node = (PWE_WINDOW_NODE)isLeaf->Node;
313 
314  if (context->TreeNewSortOrder == NoSortOrder)
315  isLeaf->IsLeaf = !node->HasChildren;
316  else
317  isLeaf->IsLeaf = TRUE;
318  }
319  return TRUE;
320  case TreeNewGetCellText:
321  {
322  PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1;
323 
324  node = (PWE_WINDOW_NODE)getCellText->Node;
325 
326  switch (getCellText->Id)
327  {
328  case WEWNTLC_CLASS:
329  PhInitializeStringRef(&getCellText->Text, node->WindowClass);
330  break;
331  case WEWNTLC_HANDLE:
333  PhInitializeStringRef(&getCellText->Text, node->WindowHandleString);
334  break;
335  case WEWNTLC_TEXT:
336  getCellText->Text = PhGetStringRef(node->WindowText);
337  break;
338  case WEWNTLC_THREAD:
339  if (!node->ThreadString)
340  node->ThreadString = PhGetClientIdName(&node->ClientId);
341  getCellText->Text = PhGetStringRef(node->ThreadString);
342  break;
343  default:
344  return FALSE;
345  }
346 
347  getCellText->Flags = TN_CACHE;
348  }
349  return TRUE;
350  case TreeNewGetNodeColor:
351  {
352  PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1;
353 
354  node = (PWE_WINDOW_NODE)getNodeColor->Node;
355 
356  if (!node->WindowVisible)
357  getNodeColor->ForeColor = RGB(0x55, 0x55, 0x55);
358 
359  getNodeColor->Flags = TN_CACHE;
360  }
361  return TRUE;
362  case TreeNewSortChanged:
363  {
364  TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder);
365  // Force a rebuild to sort the items.
367  }
368  return TRUE;
369  case TreeNewKeyDown:
370  {
371  PPH_TREENEW_KEY_EVENT keyEvent = Parameter1;
372 
373  switch (keyEvent->VirtualKey)
374  {
375  case 'C':
376  if (GetKeyState(VK_CONTROL) < 0)
377  SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WINDOW_COPY, 0);
378  break;
379  }
380  }
381  return TRUE;
383  {
384  SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WINDOW_PROPERTIES, 0);
385  }
386  return TRUE;
388  {
389  SendMessage(context->ParentWindowHandle, WM_WE_PLUSMINUS, 0, (LPARAM)Parameter1);
390  }
391  return FALSE;
392  case TreeNewContextMenu:
393  {
394  PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1;
395 
396  SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, MAKELONG(mouseEvent->Location.x, mouseEvent->Location.y));
397  }
398  return TRUE;
399  }
400 
401  return FALSE;
402 }
403 
405  _In_ PWE_WINDOW_TREE_CONTEXT Context
406  )
407 {
408  ULONG i;
409 
410  for (i = 0; i < Context->NodeList->Count; i++)
411  WepDestroyWindowNode(Context->NodeList->Items[i]);
412 
413  PhClearHashtable(Context->NodeHashtable);
414  PhClearList(Context->NodeList);
415  PhClearList(Context->NodeRootList);
416 }
417 
419  _In_ PWE_WINDOW_TREE_CONTEXT Context
420  )
421 {
422  PWE_WINDOW_NODE windowNode = NULL;
423  ULONG i;
424 
425  for (i = 0; i < Context->NodeList->Count; i++)
426  {
427  windowNode = Context->NodeList->Items[i];
428 
429  if (windowNode->Node.Selected)
430  return windowNode;
431  }
432 
433  return NULL;
434 }
435 
437  _In_ PWE_WINDOW_TREE_CONTEXT Context,
438  _Out_ PWE_WINDOW_NODE **Windows,
439  _Out_ PULONG NumberOfWindows
440  )
441 {
442  PPH_LIST list;
443  ULONG i;
444 
445  list = PhCreateList(2);
446 
447  for (i = 0; i < Context->NodeList->Count; i++)
448  {
449  PWE_WINDOW_NODE node = Context->NodeList->Items[i];
450 
451  if (node->Node.Selected)
452  {
453  PhAddItemList(list, node);
454  }
455  }
456 
457  *Windows = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
458  *NumberOfWindows = list->Count;
459 
460  PhDereferenceObject(list);
461 }