Process Hacker
wnddlg.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Window Explorer -
3  * window tree dialog
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 #include <windowsx.h>
26 
27 typedef struct _WINDOWS_CONTEXT
28 {
29  HWND TreeNewHandle;
30  WE_WINDOW_TREE_CONTEXT TreeContext;
31  WE_WINDOW_SELECTOR Selector;
32 
33  PH_LAYOUT_MANAGER LayoutManager;
34 
35  HWND HighlightingWindow;
36  ULONG HighlightingWindowCount;
38 
40  _In_ PVOID Parameter
41  );
42 
43 INT_PTR CALLBACK WepWindowsDlgProc(
44  _In_ HWND hwndDlg,
45  _In_ UINT uMsg,
46  _In_ WPARAM wParam,
47  _In_ LPARAM lParam
48  );
49 
50 static RECT MinimumSize = { -1, -1, -1, -1 };
51 
53  _In_ HWND ParentWindowHandle,
54  _In_ PWE_WINDOW_SELECTOR Selector
55  )
56 {
57  PWINDOWS_CONTEXT context;
58 
59  context = PhAllocate(sizeof(WINDOWS_CONTEXT));
60  memset(context, 0, sizeof(WINDOWS_CONTEXT));
61  memcpy(&context->Selector, Selector, sizeof(WE_WINDOW_SELECTOR));
62 
64 }
65 
67  _In_ PVOID Parameter
68  )
69 {
70  HWND hwnd;
71  PWINDOWS_CONTEXT context = Parameter;
72 
73  hwnd = CreateDialogParam(
75  MAKEINTRESOURCE(IDD_WNDLIST),
78  (LPARAM)context
79  );
80  ShowWindow(hwnd, SW_SHOW);
81 }
82 
84  _In_ PWE_WINDOW_SELECTOR Selector
85  )
86 {
87  switch (Selector->Type)
88  {
90  PhDereferenceObject(Selector->Desktop.DesktopName);
91  break;
92  }
93 }
94 
96  _In_ PWE_WINDOW_NODE Node
97  )
98 {
99  HWND hwnd;
100  ULONG threadId;
101  ULONG processId;
102 
103  hwnd = Node->WindowHandle;
104 
105  GetClassName(hwnd, Node->WindowClass, sizeof(Node->WindowClass) / sizeof(WCHAR));
106  Node->WindowText = PhGetWindowText(hwnd);
107 
108  if (!Node->WindowText)
109  Node->WindowText = PhReferenceEmptyString();
110 
111  threadId = GetWindowThreadProcessId(hwnd, &processId);
112  Node->ClientId.UniqueProcess = UlongToHandle(processId);
113  Node->ClientId.UniqueThread = UlongToHandle(threadId);
114 
115  Node->WindowVisible = !!IsWindowVisible(hwnd);
116  Node->HasChildren = !!FindWindowEx(hwnd, NULL, NULL, NULL);
117 }
118 
120  _In_ PWE_WINDOW_TREE_CONTEXT Context,
121  _In_opt_ PWE_WINDOW_NODE ParentNode,
122  _In_ HWND hwnd
123  )
124 {
125  PWE_WINDOW_NODE childNode;
126 
127  childNode = WeAddWindowNode(Context);
128  childNode->WindowHandle = hwnd;
129  WepFillWindowInfo(childNode);
130 
131  childNode->Node.Expanded = FALSE;
132 
133  if (ParentNode)
134  {
135  // This is a child node.
136  childNode->Parent = ParentNode;
137  PhAddItemList(ParentNode->Children, childNode);
138  }
139  else
140  {
141  // This is a root node.
142  PhAddItemList(Context->NodeRootList, childNode);
143  }
144 }
145 
147  _In_ PWINDOWS_CONTEXT Context,
148  _In_opt_ PWE_WINDOW_NODE ParentNode,
149  _In_ HWND hwnd,
150  _In_opt_ HANDLE FilterProcessId,
151  _In_opt_ HANDLE FilterThreadId
152  )
153 {
154  HWND childWindow = NULL;
155  ULONG i = 0;
156 
157  // We use FindWindowEx because EnumWindows doesn't return Metro app windows.
158  // Set a reasonable limit to prevent infinite loops.
159  while (i < 0x800 && (childWindow = FindWindowEx(hwnd, childWindow, NULL, NULL)))
160  {
161  ULONG processId;
162  ULONG threadId;
163 
164  threadId = GetWindowThreadProcessId(childWindow, &processId);
165 
166  if (
167  (!FilterProcessId || UlongToHandle(processId) == FilterProcessId) &&
168  (!FilterThreadId || UlongToHandle(threadId) == FilterThreadId)
169  )
170  {
171  WepAddChildWindowNode(&Context->TreeContext, ParentNode, childWindow);
172  }
173 
174  i++;
175  }
176 }
177 
179  _In_ HWND hwnd,
180  _In_ LPARAM lParam
181  )
182 {
183  PWINDOWS_CONTEXT context = (PWINDOWS_CONTEXT)lParam;
184 
185  WepAddChildWindowNode(&context->TreeContext, NULL, hwnd);
186 
187  return TRUE;
188 }
189 
191  _In_ PWINDOWS_CONTEXT Context,
192  _In_ PWSTR DesktopName
193  )
194 {
195  HDESK desktopHandle;
196 
197  if (desktopHandle = OpenDesktop(DesktopName, 0, FALSE, DESKTOP_ENUMERATE))
198  {
199  EnumDesktopWindows(desktopHandle, WepEnumDesktopWindowsProc, (LPARAM)Context);
200  CloseDesktop(desktopHandle);
201  }
202 }
203 
205  _In_ PWINDOWS_CONTEXT Context
206  )
207 {
208  TreeNew_SetRedraw(Context->TreeNewHandle, FALSE);
209  WeClearWindowTree(&Context->TreeContext);
210  TreeNew_NodesStructured(Context->TreeNewHandle);
211 
212  switch (Context->Selector.Type)
213  {
214  case WeWindowSelectorAll:
215  {
216  PWE_WINDOW_NODE desktopNode;
217 
218  desktopNode = WeAddWindowNode(&Context->TreeContext);
219  desktopNode->WindowHandle = GetDesktopWindow();
220  WepFillWindowInfo(desktopNode);
221 
222  PhAddItemList(Context->TreeContext.NodeRootList, desktopNode);
223 
224  WepAddChildWindows(Context, desktopNode, desktopNode->WindowHandle, NULL, NULL);
225 
226  desktopNode->HasChildren = TRUE;
227  desktopNode->Opened = TRUE;
228  }
229  break;
231  {
232  WepAddChildWindows(Context, NULL, GetDesktopWindow(), NULL, Context->Selector.Thread.ThreadId);
233  }
234  break;
236  {
237  WepAddChildWindows(Context, NULL, GetDesktopWindow(), Context->Selector.Process.ProcessId, NULL);
238  }
239  break;
241  {
242  WepAddDesktopWindows(Context, Context->Selector.Desktop.DesktopName->Buffer);
243  }
244  break;
245  }
246 
247  TreeNew_SetRedraw(Context->TreeNewHandle, TRUE);
248 }
249 
251  _In_ PWE_WINDOW_SELECTOR Selector
252  )
253 {
254  PPH_STRING title;
255  CLIENT_ID clientId;
256  PPH_STRING clientIdName;
257 
258  switch (Selector->Type)
259  {
260  case WeWindowSelectorAll:
261  {
262  return PhCreateString(L"Windows - All");
263  }
264  break;
266  {
267  return PhFormatString(L"Windows - Thread %lu", HandleToUlong(Selector->Thread.ThreadId));
268  }
269  break;
271  {
272  clientId.UniqueProcess = Selector->Process.ProcessId;
273  clientId.UniqueThread = NULL;
274  clientIdName = PhGetClientIdName(&clientId);
275 
276  title = PhConcatStrings2(L"Windows - ", clientIdName->Buffer);
277  PhDereferenceObject(clientIdName);
278 
279  return title;
280  }
281  break;
283  {
284  return PhFormatString(L"Windows - Desktop \"%s\"", Selector->Desktop.DesktopName->Buffer);
285  }
286  break;
287  default:
288  return PhCreateString(L"Windows");
289  }
290 }
291 
292 INT_PTR CALLBACK WepWindowsDlgProc(
293  _In_ HWND hwndDlg,
294  _In_ UINT uMsg,
295  _In_ WPARAM wParam,
296  _In_ LPARAM lParam
297  )
298 {
299  PWINDOWS_CONTEXT context;
300 
301  if (uMsg == WM_INITDIALOG)
302  {
303  context = (PWINDOWS_CONTEXT)lParam;
304  SetProp(hwndDlg, L"Context", (HANDLE)context);
305  }
306  else
307  {
308  context = (PWINDOWS_CONTEXT)GetProp(hwndDlg, L"Context");
309 
310  if (uMsg == WM_DESTROY)
311  RemoveProp(hwndDlg, L"Context");
312  }
313 
314  if (!context)
315  return FALSE;
316 
317  switch (uMsg)
318  {
319  case WM_INITDIALOG:
320  {
321  PPH_STRING windowTitle;
322  PH_RECTANGLE windowRectangle;
323 
324  context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST);
325  WeInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext);
326 
327  PhRegisterDialog(hwndDlg);
328 
329  PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
330  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL);
331 
332  if (MinimumSize.left == -1)
333  {
334  RECT rect;
335 
336  rect.left = 0;
337  rect.top = 0;
338  rect.right = 160;
339  rect.bottom = 100;
340  MapDialogRect(hwndDlg, &rect);
341  MinimumSize = rect;
342  MinimumSize.left = 0;
343  }
344 
345  // Set up the window position and size.
346 
349  PhAdjustRectangleToWorkingArea(hwndDlg, &windowRectangle);
350 
351  MoveWindow(hwndDlg, windowRectangle.Left, windowRectangle.Top,
352  windowRectangle.Width, windowRectangle.Height, FALSE);
353 
354  // Implement cascading by saving an offsetted rectangle.
355  windowRectangle.Left += 20;
356  windowRectangle.Top += 20;
358 
359  windowTitle = WepGetWindowTitleForSelector(&context->Selector);
360  SetWindowText(hwndDlg, windowTitle->Buffer);
361  PhDereferenceObject(windowTitle);
362 
363  WepRefreshWindows(context);
364  }
365  break;
366  case WM_DESTROY:
367  {
369 
370  PhDeleteLayoutManager(&context->LayoutManager);
371  PhUnregisterDialog(hwndDlg);
372 
373  WeDeleteWindowTree(&context->TreeContext);
374  WepDeleteWindowSelector(&context->Selector);
375  PhFree(context);
376  }
377  break;
378  case WM_COMMAND:
379  {
380  switch (LOWORD(wParam))
381  {
382  case IDCANCEL:
383  //case IDOK:
384  DestroyWindow(hwndDlg);
385  break;
386  case IDC_REFRESH:
387  WepRefreshWindows(context);
388  break;
389  case ID_SHOWCONTEXTMENU:
390  {
391  POINT point;
392  PWE_WINDOW_NODE *windows;
393  ULONG numberOfWindows;
394  PPH_EMENU menu;
395 
396  point.x = (SHORT)LOWORD(lParam);
397  point.y = (SHORT)HIWORD(lParam);
398 
400  &context->TreeContext,
401  &windows,
402  &numberOfWindows
403  );
404 
405  if (numberOfWindows != 0)
406  {
407  menu = PhCreateEMenu();
408  PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_WINDOW), 0);
410 
411  if (numberOfWindows == 1)
412  {
413  WINDOWPLACEMENT placement = { sizeof(placement) };
414  BYTE alpha;
415  ULONG flags;
416  ULONG i;
417  ULONG id;
418 
419  // State
420 
421  GetWindowPlacement(windows[0]->WindowHandle, &placement);
422 
423  if (placement.showCmd == SW_MINIMIZE)
425  else if (placement.showCmd == SW_MAXIMIZE)
427  else if (placement.showCmd == SW_NORMAL)
429 
430  // Visible
431 
433  (GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_VISIBLE) ? PH_EMENU_CHECKED : 0);
434 
435  // Enabled
436 
437  PhSetFlagsEMenuItem(menu, ID_WINDOW_ENABLED, PH_EMENU_CHECKED,
438  !(GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_DISABLED) ? PH_EMENU_CHECKED : 0);
439 
440  // Always on Top
441 
442  PhSetFlagsEMenuItem(menu, ID_WINDOW_ALWAYSONTOP, PH_EMENU_CHECKED,
443  (GetWindowLong(windows[0]->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST) ? PH_EMENU_CHECKED : 0);
444 
445  // Opacity
446 
447  if (GetLayeredWindowAttributes(windows[0]->WindowHandle, NULL, &alpha, &flags))
448  {
449  if (!(flags & LWA_ALPHA))
450  alpha = 255;
451  }
452  else
453  {
454  alpha = 255;
455  }
456 
457  if (alpha == 255)
458  {
459  id = ID_OPACITY_OPAQUE;
460  }
461  else
462  {
463  id = 0;
464 
465  // Due to integer division, we cannot use simple arithmetic to calculate which menu item to check.
466  for (i = 0; i < 10; i++)
467  {
468  if (alpha == (BYTE)(255 * (i + 1) / 10))
469  {
470  id = ID_OPACITY_10 + i;
471  break;
472  }
473  }
474  }
475 
476  if (id != 0)
477  {
478  PhSetFlagsEMenuItem(menu, id, PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK,
479  PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK);
480  }
481  }
482  else
483  {
486  }
487 
489  PhDestroyEMenu(menu);
490  }
491  }
492  break;
494  {
495  PWE_WINDOW_NODE selectedNode;
496 
497  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
498  {
499  WINDOWPLACEMENT placement = { sizeof(placement) };
500 
501  GetWindowPlacement(selectedNode->WindowHandle, &placement);
502 
503  if (placement.showCmd == SW_MINIMIZE)
504  ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE);
505  else
506  SetForegroundWindow(selectedNode->WindowHandle);
507  }
508  }
509  break;
510  case ID_WINDOW_RESTORE:
511  {
512  PWE_WINDOW_NODE selectedNode;
513 
514  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
515  {
516  ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE);
517  }
518  }
519  break;
520  case ID_WINDOW_MINIMIZE:
521  {
522  PWE_WINDOW_NODE selectedNode;
523 
524  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
525  {
526  ShowWindowAsync(selectedNode->WindowHandle, SW_MINIMIZE);
527  }
528  }
529  break;
530  case ID_WINDOW_MAXIMIZE:
531  {
532  PWE_WINDOW_NODE selectedNode;
533 
534  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
535  {
536  ShowWindowAsync(selectedNode->WindowHandle, SW_MAXIMIZE);
537  }
538  }
539  break;
540  case ID_WINDOW_CLOSE:
541  {
542  PWE_WINDOW_NODE selectedNode;
543 
544  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
545  {
546  PostMessage(selectedNode->WindowHandle, WM_CLOSE, 0, 0);
547  }
548  }
549  break;
550  case ID_WINDOW_VISIBLE:
551  {
552  PWE_WINDOW_NODE selectedNode;
553 
554  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
555  {
556  if (IsWindowVisible(selectedNode->WindowHandle))
557  {
558  selectedNode->WindowVisible = FALSE;
559  ShowWindowAsync(selectedNode->WindowHandle, SW_HIDE);
560  }
561  else
562  {
563  selectedNode->WindowVisible = TRUE;
564  ShowWindowAsync(selectedNode->WindowHandle, SW_SHOW);
565  }
566 
568  TreeNew_InvalidateNode(context->TreeNewHandle, &selectedNode->Node);
569  }
570  }
571  break;
572  case ID_WINDOW_ENABLED:
573  {
574  PWE_WINDOW_NODE selectedNode;
575 
576  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
577  {
578  EnableWindow(selectedNode->WindowHandle, !IsWindowEnabled(selectedNode->WindowHandle));
579  }
580  }
581  break;
583  {
584  PWE_WINDOW_NODE selectedNode;
585 
586  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
587  {
588  LOGICAL topMost;
589 
590  topMost = GetWindowLong(selectedNode->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST;
591  SetWindowPos(selectedNode->WindowHandle, topMost ? HWND_NOTOPMOST : HWND_TOPMOST,
592  0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
593  }
594  }
595  break;
596  case ID_OPACITY_10:
597  case ID_OPACITY_20:
598  case ID_OPACITY_30:
599  case ID_OPACITY_40:
600  case ID_OPACITY_50:
601  case ID_OPACITY_60:
602  case ID_OPACITY_70:
603  case ID_OPACITY_80:
604  case ID_OPACITY_90:
605  case ID_OPACITY_OPAQUE:
606  {
607  PWE_WINDOW_NODE selectedNode;
608 
609  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
610  {
611  ULONG opacity;
612 
613  opacity = ((ULONG)LOWORD(wParam) - ID_OPACITY_10) + 1;
614 
615  if (opacity == 10)
616  {
617  // Remove the WS_EX_LAYERED bit since it is not needed.
618  PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, 0);
619  RedrawWindow(selectedNode->WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
620  }
621  else
622  {
623  // Add the WS_EX_LAYERED bit so opacity will work.
624  PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED);
625  SetLayeredWindowAttributes(selectedNode->WindowHandle, 0, (BYTE)(255 * opacity / 10), LWA_ALPHA);
626  }
627  }
628  }
629  break;
630  case ID_WINDOW_HIGHLIGHT:
631  {
632  PWE_WINDOW_NODE selectedNode;
633 
634  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
635  {
636  if (context->HighlightingWindow)
637  {
638  if (context->HighlightingWindowCount & 1)
639  WeInvertWindowBorder(context->HighlightingWindow);
640  }
641 
642  context->HighlightingWindow = selectedNode->WindowHandle;
643  context->HighlightingWindowCount = 10;
644  SetTimer(hwndDlg, 9, 100, NULL);
645  }
646  }
647  break;
649  {
650  PWE_WINDOW_NODE selectedNode;
651  PPH_PROCESS_ITEM processItem;
652  PPH_PROCESS_PROPCONTEXT propContext;
653 
654  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
655  {
656  if (processItem = PhReferenceProcessItem(selectedNode->ClientId.UniqueProcess))
657  {
658  if (propContext = PhCreateProcessPropContext(WE_PhMainWndHandle, processItem))
659  {
660  PhSetSelectThreadIdProcessPropContext(propContext, selectedNode->ClientId.UniqueThread);
661  PhShowProcessProperties(propContext);
662  PhDereferenceObject(propContext);
663  }
664 
665  PhDereferenceObject(processItem);
666  }
667  else
668  {
669  PhShowError(hwndDlg, L"The process does not exist.");
670  }
671  }
672  }
673  break;
675  {
676  PWE_WINDOW_NODE selectedNode;
677 
678  if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
680  }
681  break;
682  case ID_WINDOW_COPY:
683  {
684  PPH_STRING text;
685 
686  text = PhGetTreeNewText(context->TreeNewHandle, 0);
687  PhSetClipboardString(hwndDlg, &text->sr);
688  PhDereferenceObject(text);
689  }
690  break;
691  }
692  }
693  break;
694  case WM_TIMER:
695  {
696  switch (wParam)
697  {
698  case 9:
699  {
700  WeInvertWindowBorder(context->HighlightingWindow);
701 
702  if (--context->HighlightingWindowCount == 0)
703  KillTimer(hwndDlg, 9);
704  }
705  break;
706  }
707  }
708  break;
709  case WM_SIZE:
710  {
711  PhLayoutManagerLayout(&context->LayoutManager);
712  }
713  break;
714  case WM_SIZING:
715  {
716  PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom);
717  }
718  break;
719  case WM_WE_PLUSMINUS:
720  {
721  PWE_WINDOW_NODE node = (PWE_WINDOW_NODE)lParam;
722 
723  if (!node->Opened)
724  {
725  TreeNew_SetRedraw(context->TreeNewHandle, FALSE);
726  WepAddChildWindows(context, node, node->WindowHandle, NULL, NULL);
727  node->Opened = TRUE;
728  TreeNew_SetRedraw(context->TreeNewHandle, TRUE);
729  }
730  }
731  break;
732  }
733 
734  return FALSE;
735 }