Process Hacker
procgrp.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * process grouping
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 <procgrp.h>
25 
26 typedef struct _PHP_PROCESS_DATA
27 {
28  PPH_PROCESS_NODE Process;
29  LIST_ENTRY ListEntry;
30  BOOLEAN HasWindow;
32 
34  _In_ PPH_LIST Processes
35  )
36 {
37  PPH_LIST processDataList;
38  ULONG i;
39 
40  processDataList = PhCreateList(Processes->Count);
41 
42  for (i = 0; i < Processes->Count; i++)
43  {
44  PPH_PROCESS_NODE process = Processes->Items[i];
45  PPHP_PROCESS_DATA processData;
46 
48  continue;
49 
50  processData = PhAllocate(sizeof(PHP_PROCESS_DATA));
51  memset(processData, 0, sizeof(PHP_PROCESS_DATA));
52  processData->Process = process;
53  PhAddItemList(processDataList, processData);
54  }
55 
56  return processDataList;
57 }
58 
60  _In_ PPH_LIST List
61  )
62 {
63  ULONG i;
64 
65  for (i = 0; i < List->Count; i++)
66  {
67  PPHP_PROCESS_DATA processData = List->Items[i];
68  PhFree(processData);
69  }
70 
71  PhDereferenceObject(List);
72 }
73 
75  _In_ PPH_LIST List,
76  _Out_ PLIST_ENTRY ListHead
77  )
78 {
79  ULONG i;
80 
81  InitializeListHead(ListHead);
82 
83  for (i = 0; i < List->Count; i++)
84  {
85  PPHP_PROCESS_DATA processData = List->Items[i];
86  InsertTailList(ListHead, &processData->ListEntry);
87  }
88 }
89 
91  _In_ PPH_LIST List,
92  _Out_ PPH_HASHTABLE *Hashtable
93  )
94 {
95  PPH_HASHTABLE hashtable;
96  ULONG i;
97 
98  hashtable = PhCreateSimpleHashtable(List->Count);
99 
100  for (i = 0; i < List->Count; i++)
101  {
102  PPHP_PROCESS_DATA processData = List->Items[i];
103  PhAddItemSimpleHashtable(hashtable, processData->Process->ProcessId, processData);
104  }
105 
106  *Hashtable = hashtable;
107 }
108 
109 typedef struct _QUERY_WINDOWS_CONTEXT
110 {
111  PPH_HASHTABLE ProcessDataHashtable;
113 
115  _In_ HWND hwnd,
116  _In_ LPARAM lParam
117  )
118 {
120  ULONG processId;
121  PPHP_PROCESS_DATA processData;
122  HWND parentWindow;
123 
124  if (!IsWindowVisible(hwnd))
125  return TRUE;
126 
127  GetWindowThreadProcessId(hwnd, &processId);
128  processData = PhFindItemSimpleHashtable2(context->ProcessDataHashtable, UlongToHandle(processId));
129 
130  if (!processData || processData->HasWindow)
131  return TRUE;
132 
133  if (!((parentWindow = GetParent(hwnd)) && IsWindowVisible(parentWindow)) && // skip windows with a visible parent
134  PhGetWindowTextEx(hwnd, PH_GET_WINDOW_TEXT_INTERNAL | PH_GET_WINDOW_TEXT_LENGTH_ONLY, NULL) != 0) // skip windows with no title
135  {
136  processData->HasWindow = TRUE;
137  }
138 
139  return TRUE;
140 }
141 
143  _In_ PPH_PROCESS_ITEM ProcessItem,
144  _In_ ULONG Flags
145  )
146 {
147  if (Flags & PH_GROUP_PROCESSES_FILE_PATH)
148  return ProcessItem->FileName;
149  else
150  return ProcessItem->ProcessName;
151 }
152 
154  _In_ PPH_STRING FileName,
155  _In_ PPH_STRING UserName,
156  _In_ PPH_PROCESS_ITEM ProcessItem,
157  _In_ ULONG Flags
158  )
159 {
160  PPH_STRING otherFileName;
161  PPH_STRING otherUserName;
162 
163  otherFileName = PhpGetRelevantFileName(ProcessItem, Flags);
164  otherUserName = ProcessItem->UserName;
165 
166  return
167  otherFileName && PhEqualString(otherFileName, FileName, TRUE) &&
168  otherUserName && PhEqualString(otherUserName, UserName, TRUE);
169 }
170 
172  _In_ PPHP_PROCESS_DATA ProcessData,
173  _In_ PPH_HASHTABLE ProcessDataHashtable,
174  _In_ ULONG Flags
175  )
176 {
177  PPH_PROCESS_NODE root;
178  PPHP_PROCESS_DATA rootProcessData;
179  PPH_PROCESS_NODE parent;
180  PPHP_PROCESS_DATA processData;
181  PPH_STRING fileName;
182  PPH_STRING userName;
183 
184  root = ProcessData->Process;
185  rootProcessData = ProcessData;
186  fileName = PhpGetRelevantFileName(ProcessData->Process->ProcessItem, Flags);
187  userName = ProcessData->Process->ProcessItem->UserName;
188 
189  if (ProcessData->HasWindow)
190  return rootProcessData;
191 
192  while (parent = root->Parent)
193  {
194  if ((processData = PhFindItemSimpleHashtable2(ProcessDataHashtable, parent->ProcessId)) &&
195  PhpEqualFileNameAndUserName(fileName, userName, parent->ProcessItem, Flags))
196  {
197  root = parent;
198  rootProcessData = processData;
199 
200  if (processData->HasWindow)
201  break;
202  }
203  else
204  {
205  break;
206  }
207  }
208 
209  return rootProcessData;
210 }
211 
213  _In_ PPHP_PROCESS_DATA ProcessData,
214  _Inout_ PPH_LIST List
215  )
216 {
217  PhReferenceObject(ProcessData->Process->ProcessItem);
218  PhAddItemList(List, ProcessData->Process->ProcessItem);
219  RemoveEntryList(&ProcessData->ListEntry);
220 }
221 
223  _In_ PPHP_PROCESS_DATA ProcessData,
224  _Inout_ PPH_LIST List,
225  _In_ PPH_HASHTABLE ProcessDataHashtable,
226  _In_ ULONG Flags
227  )
228 {
229  PPH_STRING fileName;
230  PPH_STRING userName;
231  ULONG i;
232 
233  PhpAddGroupMember(ProcessData, List);
234  fileName = PhpGetRelevantFileName(ProcessData->Process->ProcessItem, Flags);
235  userName = ProcessData->Process->ProcessItem->UserName;
236 
237  for (i = 0; i < ProcessData->Process->Children->Count; i++)
238  {
239  PPH_PROCESS_NODE node = ProcessData->Process->Children->Items[i];
240  PPHP_PROCESS_DATA processData;
241 
242  if ((processData = PhFindItemSimpleHashtable2(ProcessDataHashtable, node->ProcessId)) &&
243  PhpEqualFileNameAndUserName(fileName, userName, node->ProcessItem, Flags) &&
244  node->ProcessItem->UserName && PhEqualString(node->ProcessItem->UserName, userName, TRUE) &&
245  !processData->HasWindow)
246  {
247  PhpAddGroupMembersFromRoot(processData, List, ProcessDataHashtable, Flags);
248  }
249  }
250 }
251 
253  _In_opt_ PPH_SORT_LIST_FUNCTION SortListFunction,
254  _In_opt_ PVOID Context,
255  _In_ ULONG MaximumGroups,
256  _In_ ULONG Flags
257  )
258 {
259  PPH_LIST processList;
260  PPH_LIST processDataList;
261  LIST_ENTRY processDataListHead; // We will be removing things from this list as we group processes together
262  PPH_HASHTABLE processDataHashtable; // Process ID to process data hashtable
263  QUERY_WINDOWS_CONTEXT queryWindowsContext;
264  PPH_LIST processGroupList;
265 
266  // We group together processes that share a common ancestor and have the same file name, where the ancestor must
267  // have a visible window and all other processes in the group do not have a visible window. All processes in the
268  // group must have the same user name. All ancestors up to the lowest common ancestor must have the same file name
269  // and user name.
270 
271  processList = PhDuplicateProcessNodeList();
272 
273  if (SortListFunction)
274  SortListFunction(processList, Context);
275 
276  processDataList = PhpCreateProcessDataList(processList);
277  PhDereferenceObject(processList);
278  PhpProcessDataListToLinkedList(processDataList, &processDataListHead);
279  PhpProcessDataListToHashtable(processDataList, &processDataHashtable);
280 
281  queryWindowsContext.ProcessDataHashtable = processDataHashtable;
282  PhEnumChildWindows(NULL, 0x800, PhpQueryWindowsEnumWindowsProc, (LPARAM)&queryWindowsContext);
283 
284  processGroupList = PhCreateList(10);
285 
286  while (processDataListHead.Flink != &processDataListHead && processGroupList->Count < MaximumGroups)
287  {
288  PPHP_PROCESS_DATA processData = CONTAINING_RECORD(processDataListHead.Flink, PHP_PROCESS_DATA, ListEntry);
289  PPH_PROCESS_GROUP processGroup;
290  PPH_STRING fileName;
291  PPH_STRING userName;
292 
293  processGroup = PhAllocate(sizeof(PH_PROCESS_GROUP));
294  processGroup->Processes = PhCreateList(4);
295  fileName = PhpGetRelevantFileName(processData->Process->ProcessItem, Flags);
296  userName = processData->Process->ProcessItem->UserName;
297 
298  if (!fileName || !userName || (Flags & PH_GROUP_PROCESSES_DONT_GROUP))
299  {
300  processGroup->Representative = processData->Process->ProcessItem;
301  PhpAddGroupMember(processData, processGroup->Processes);
302  }
303  else
304  {
305  processData = PhpFindGroupRoot(processData, processDataHashtable, Flags);
306  processGroup->Representative = processData->Process->ProcessItem;
307  PhpAddGroupMembersFromRoot(processData, processGroup->Processes, processDataHashtable, Flags);
308  }
309 
310  PhAddItemList(processGroupList, processGroup);
311  }
312 
313  PhDereferenceObject(processDataHashtable);
314  PhpDestroyProcessDataList(processDataList);
315 
316  return processGroupList;
317 }
318 
320  _In_ PPH_LIST List
321  )
322 {
323  ULONG i;
324 
325  for (i = 0; i < List->Count; i++)
326  {
327  PPH_PROCESS_GROUP processGroup = List->Items[i];
328 
329  PhDereferenceObjects(processGroup->Processes->Items, processGroup->Processes->Count);
330  PhDereferenceObject(processGroup->Processes);
331  PhFree(processGroup);
332  }
333 
334  PhDereferenceObject(List);
335 }