Process Hacker
wndprp.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Window Explorer -
3  * window properties
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 // Since the main message loop doesn't support property sheets,
24 // we need a separate thread to run property sheets on.
25 
26 #include "wndexp.h"
27 #include "resource.h"
28 #include <symprv.h>
29 #include <windowsx.h>
30 
31 #define NUMBER_OF_PAGES 4
32 #define WEM_RESOLVE_DONE (WM_APP + 1234)
33 
34 typedef struct _WINDOW_PROPERTIES_CONTEXT
35 {
36  LONG RefCount;
37 
38  HWND ParentWindowHandle;
39 
40  HWND WindowHandle;
41  CLIENT_ID ClientId;
42  PH_INITONCE SymbolProviderInitOnce;
43  PPH_SYMBOL_PROVIDER SymbolProvider;
44  LIST_ENTRY ResolveListHead;
45  PH_QUEUED_LOCK ResolveListLock;
46 
47  PPH_STRING WndProcSymbol;
48  ULONG WndProcResolving;
49  PPH_STRING DlgProcSymbol;
50  ULONG DlgProcResolving;
51  PPH_STRING ClassWndProcSymbol;
52  ULONG ClassWndProcResolving;
53 
54  BOOLEAN HookDataValid;
55  BOOLEAN HookDataSuccess;
56  ULONG_PTR WndProc;
57  ULONG_PTR DlgProc;
58  WNDCLASSEX ClassInfo;
60 
61 typedef struct _SYMBOL_RESOLVE_CONTEXT
62 {
63  LIST_ENTRY ListEntry;
64  ULONG64 Address;
65  PPH_STRING Symbol;
66  PH_SYMBOL_RESOLVE_LEVEL ResolveLevel;
67  HWND NotifyWindow;
69  ULONG Id;
71 
72 typedef struct _STRING_INTEGER_PAIR
73 {
74  PWSTR String;
75  ULONG Integer;
77 
79  _Inout_ PWINDOW_PROPERTIES_CONTEXT Context
80  );
81 
83  _Inout_ PWINDOW_PROPERTIES_CONTEXT Context
84  );
85 
87  _In_ PWINDOW_PROPERTIES_CONTEXT Context
88  );
89 
90 INT CALLBACK WepPropSheetProc(
91  _In_ HWND hwndDlg,
92  _In_ UINT uMsg,
93  _In_ LPARAM lParam
94  );
95 
96 LRESULT CALLBACK WepPropSheetWndProc(
97  _In_ HWND hwnd,
98  _In_ UINT uMsg,
99  _In_ WPARAM wParam,
100  _In_ LPARAM lParam
101  );
102 
103 HPROPSHEETPAGE WepCommonCreatePage(
104  _In_ PWINDOW_PROPERTIES_CONTEXT Context,
105  _In_ PWSTR Template,
106  _In_ DLGPROC DlgProc
107  );
108 
109 INT CALLBACK WepCommonPropPageProc(
110  _In_ HWND hwnd,
111  _In_ UINT uMsg,
112  _In_ LPPROPSHEETPAGE ppsp
113  );
114 
115 NTSTATUS WepPropertiesThreadStart(
116  _In_ PVOID Parameter
117  );
118 
119 INT_PTR CALLBACK WepWindowGeneralDlgProc(
120  _In_ HWND hwndDlg,
121  _In_ UINT uMsg,
122  _In_ WPARAM wParam,
123  _In_ LPARAM lParam
124  );
125 
126 INT_PTR CALLBACK WepWindowStylesDlgProc(
127  _In_ HWND hwndDlg,
128  _In_ UINT uMsg,
129  _In_ WPARAM wParam,
130  _In_ LPARAM lParam
131  );
132 
133 INT_PTR CALLBACK WepWindowClassDlgProc(
134  _In_ HWND hwndDlg,
135  _In_ UINT uMsg,
136  _In_ WPARAM wParam,
137  _In_ LPARAM lParam
138  );
139 
140 INT_PTR CALLBACK WepWindowPropertiesDlgProc(
141  _In_ HWND hwndDlg,
142  _In_ UINT uMsg,
143  _In_ WPARAM wParam,
144  _In_ LPARAM lParam
145  );
146 
147 #define DEFINE_PAIR(Symbol) { L#Symbol, Symbol }
148 
149 static STRING_INTEGER_PAIR WepStylePairs[] =
150 {
151  DEFINE_PAIR(WS_POPUP),
152  DEFINE_PAIR(WS_CHILD),
153  DEFINE_PAIR(WS_MINIMIZE),
154  DEFINE_PAIR(WS_VISIBLE),
155  DEFINE_PAIR(WS_DISABLED),
156  DEFINE_PAIR(WS_CLIPSIBLINGS),
157  DEFINE_PAIR(WS_CLIPCHILDREN),
158  DEFINE_PAIR(WS_MAXIMIZE),
159  DEFINE_PAIR(WS_BORDER),
160  DEFINE_PAIR(WS_DLGFRAME),
161  DEFINE_PAIR(WS_VSCROLL),
162  DEFINE_PAIR(WS_HSCROLL),
163  DEFINE_PAIR(WS_SYSMENU),
164  DEFINE_PAIR(WS_THICKFRAME),
165  DEFINE_PAIR(WS_GROUP),
166  DEFINE_PAIR(WS_TABSTOP),
167  DEFINE_PAIR(WS_MINIMIZEBOX),
168  DEFINE_PAIR(WS_MAXIMIZEBOX)
169 };
170 
171 static STRING_INTEGER_PAIR WepExtendedStylePairs[] =
172 {
173  DEFINE_PAIR(WS_EX_DLGMODALFRAME),
174  DEFINE_PAIR(WS_EX_NOPARENTNOTIFY),
175  DEFINE_PAIR(WS_EX_TOPMOST),
176  DEFINE_PAIR(WS_EX_ACCEPTFILES),
177  DEFINE_PAIR(WS_EX_TRANSPARENT),
178  DEFINE_PAIR(WS_EX_MDICHILD),
179  DEFINE_PAIR(WS_EX_TOOLWINDOW),
180  DEFINE_PAIR(WS_EX_WINDOWEDGE),
181  DEFINE_PAIR(WS_EX_CLIENTEDGE),
182  DEFINE_PAIR(WS_EX_CONTEXTHELP),
183  DEFINE_PAIR(WS_EX_RIGHT),
184  DEFINE_PAIR(WS_EX_RTLREADING),
185  DEFINE_PAIR(WS_EX_LEFTSCROLLBAR),
186  DEFINE_PAIR(WS_EX_CONTROLPARENT),
187  DEFINE_PAIR(WS_EX_STATICEDGE),
188  DEFINE_PAIR(WS_EX_APPWINDOW),
189  DEFINE_PAIR(WS_EX_LAYERED),
190  DEFINE_PAIR(WS_EX_NOINHERITLAYOUT),
191  DEFINE_PAIR(WS_EX_LAYOUTRTL),
192  DEFINE_PAIR(WS_EX_COMPOSITED),
193  DEFINE_PAIR(WS_EX_NOACTIVATE)
194 };
195 
196 static STRING_INTEGER_PAIR WepClassStylePairs[] =
197 {
198  DEFINE_PAIR(CS_VREDRAW),
199  DEFINE_PAIR(CS_HREDRAW),
200  DEFINE_PAIR(CS_DBLCLKS),
201  DEFINE_PAIR(CS_OWNDC),
202  DEFINE_PAIR(CS_CLASSDC),
203  DEFINE_PAIR(CS_PARENTDC),
204  DEFINE_PAIR(CS_NOCLOSE),
205  DEFINE_PAIR(CS_SAVEBITS),
206  DEFINE_PAIR(CS_BYTEALIGNCLIENT),
207  DEFINE_PAIR(CS_BYTEALIGNWINDOW),
208  DEFINE_PAIR(CS_GLOBALCLASS),
209  DEFINE_PAIR(CS_IME),
210  DEFINE_PAIR(CS_DROPSHADOW)
211 };
212 
219 
221  _In_ HWND ParentWindowHandle,
222  _In_ HWND WindowHandle
223  )
224 {
226  ULONG threadId;
227  ULONG processId;
228 
229  if (!WePropertiesCreateList)
230  WePropertiesCreateList = PhCreateList(4);
231  if (!WePropertiesWindowList)
232  WePropertiesWindowList = PhCreateList(4);
233 
235  {
237  PhWaitForEvent(&WePropertiesThreadReadyEvent, NULL);
238  }
239 
240  context = PhAllocate(sizeof(WINDOW_PROPERTIES_CONTEXT));
241  memset(context, 0, sizeof(WINDOW_PROPERTIES_CONTEXT));
242  context->RefCount = 1;
243  context->ParentWindowHandle = ParentWindowHandle;
244  context->WindowHandle = WindowHandle;
245 
246  processId = 0;
247  threadId = GetWindowThreadProcessId(WindowHandle, &processId);
248  context->ClientId.UniqueProcess = UlongToHandle(processId);
249  context->ClientId.UniqueThread = UlongToHandle(threadId);
250  PhInitializeInitOnce(&context->SymbolProviderInitOnce);
251  InitializeListHead(&context->ResolveListHead);
252  PhInitializeQueuedLock(&context->ResolveListLock);
253 
254  // Queue the window for creation and wake up the host thread.
255  PhAcquireQueuedLockExclusive(&WePropertiesCreateLock);
256  PhAddItemList(WePropertiesCreateList, context);
257  PhReleaseQueuedLockExclusive(&WePropertiesCreateLock);
258  PostThreadMessage(HandleToUlong(WePropertiesThreadClientId.UniqueThread), WM_NULL, 0, 0);
259 }
260 
262  _Inout_ PWINDOW_PROPERTIES_CONTEXT Context
263  )
264 {
265  _InterlockedIncrement(&Context->RefCount);
266 }
267 
269  _Inout_ PWINDOW_PROPERTIES_CONTEXT Context
270  )
271 {
272  if (_InterlockedDecrement(&Context->RefCount) == 0)
273  {
274  PLIST_ENTRY listEntry;
275 
276  PhClearReference(&Context->SymbolProvider);
277 
278  // Destroy results that have not been processed by any property pages.
279 
280  listEntry = Context->ResolveListHead.Flink;
281 
282  while (listEntry != &Context->ResolveListHead)
283  {
284  PSYMBOL_RESOLVE_CONTEXT resolveContext;
285 
286  resolveContext = CONTAINING_RECORD(listEntry, SYMBOL_RESOLVE_CONTEXT, ListEntry);
287  listEntry = listEntry->Flink;
288 
289  PhClearReference(&resolveContext->Symbol);
290  PhFree(resolveContext);
291  }
292 
293  PhClearReference(&Context->WndProcSymbol);
294  PhClearReference(&Context->DlgProcSymbol);
295  PhClearReference(&Context->ClassWndProcSymbol);
296 
297  PhFree(Context);
298  }
299 }
300 
302  _In_ PWINDOW_PROPERTIES_CONTEXT Context
303  )
304 {
305  PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) };
306  HPROPSHEETPAGE pages[NUMBER_OF_PAGES];
307 
308  propSheetHeader.dwFlags =
309  PSH_MODELESS |
310  PSH_NOAPPLYNOW |
311  PSH_NOCONTEXTHELP |
312  PSH_PROPTITLE |
313  PSH_USECALLBACK;
314  propSheetHeader.hwndParent = Context->ParentWindowHandle;
315  propSheetHeader.pszCaption = PhaFormatString(L"Window %Ix", Context->WindowHandle)->Buffer;
316  propSheetHeader.nPages = 0;
317  propSheetHeader.nStartPage = 0;
318  propSheetHeader.phpage = pages;
319  propSheetHeader.pfnCallback = WepPropSheetProc;
320 
321  // General
322  pages[propSheetHeader.nPages++] = WepCommonCreatePage(
323  Context,
324  MAKEINTRESOURCE(IDD_WNDGENERAL),
326  );
327  // Styles
328  pages[propSheetHeader.nPages++] = WepCommonCreatePage(
329  Context,
330  MAKEINTRESOURCE(IDD_WNDSTYLES),
332  );
333  // Class
334  pages[propSheetHeader.nPages++] = WepCommonCreatePage(
335  Context,
336  MAKEINTRESOURCE(IDD_WNDCLASS),
338  );
339  // Properties
340  pages[propSheetHeader.nPages++] = WepCommonCreatePage(
341  Context,
342  MAKEINTRESOURCE(IDD_WNDPROPS),
344  );
345 
346  return (HWND)PropertySheet(&propSheetHeader);
347 }
348 
349 static INT CALLBACK WepPropSheetProc(
350  _In_ HWND hwndDlg,
351  _In_ UINT uMsg,
352  _In_ LPARAM lParam
353  )
354 {
355  switch (uMsg)
356  {
357  case PSCB_INITIALIZED:
358  {
359  WNDPROC oldWndProc;
360  HWND refreshButtonHandle;
361 
362  oldWndProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC);
363  SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)WepPropSheetWndProc);
364  SetProp(hwndDlg, L"OldWndProc", (HANDLE)oldWndProc);
365 
366  // Hide the Cancel button.
367  ShowWindow(GetDlgItem(hwndDlg, IDCANCEL), SW_HIDE);
368  // Set the OK button's text to "Close".
369  SetDlgItemText(hwndDlg, IDOK, L"Close");
370  // Add the Refresh button.
371  refreshButtonHandle = CreateWindow(L"BUTTON", L"Refresh", WS_CHILD | WS_TABSTOP | WS_VISIBLE, 0, 0, 3, 3, hwndDlg, (HMENU)IDC_REFRESH,
372  PluginInstance->DllBase, NULL);
373  SendMessage(refreshButtonHandle, WM_SETFONT, (WPARAM)SendMessage(GetDlgItem(hwndDlg, IDOK), WM_GETFONT, 0, 0), FALSE);
374  }
375  break;
376  }
377 
378  return 0;
379 }
380 
381 LRESULT CALLBACK WepPropSheetWndProc(
382  _In_ HWND hwnd,
383  _In_ UINT uMsg,
384  _In_ WPARAM wParam,
385  _In_ LPARAM lParam
386  )
387 {
388  WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, L"OldWndProc");
389 
390  switch (uMsg)
391  {
392  case WM_DESTROY:
393  {
394  SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc);
395  RemoveProp(hwnd, L"OldWndProc");
396  RemoveProp(hwnd, L"Moved");
397  }
398  break;
399  case WM_SHOWWINDOW:
400  {
401  if (!GetProp(hwnd, L"Moved"))
402  {
403  // Move the Refresh button to where the OK button is, and move the OK button to
404  // where the Cancel button is.
405  // This must be done here because in the prop sheet callback the buttons are not
406  // in the right places.
407  PhCopyControlRectangle(hwnd, GetDlgItem(hwnd, IDOK), GetDlgItem(hwnd, IDC_REFRESH));
408  PhCopyControlRectangle(hwnd, GetDlgItem(hwnd, IDCANCEL), GetDlgItem(hwnd, IDOK));
409  SetProp(hwnd, L"Moved", (HANDLE)1);
410  }
411  }
412  break;
413  case WM_COMMAND:
414  {
415  switch (LOWORD(wParam))
416  {
417  case IDC_REFRESH:
418  {
419  ULONG i;
420  HWND pageHandle;
421 
422  // Broadcast the message to all property pages.
423  for (i = 0; i < NUMBER_OF_PAGES; i++)
424  {
425  if (pageHandle = PropSheet_IndexToHwnd(hwnd, i))
426  SendMessage(pageHandle, WM_COMMAND, IDC_REFRESH, 0);
427  }
428  }
429  break;
430  }
431  }
432  break;
433  }
434 
435  return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam);
436 }
437 
438 static HPROPSHEETPAGE WepCommonCreatePage(
439  _In_ PWINDOW_PROPERTIES_CONTEXT Context,
440  _In_ PWSTR Template,
441  _In_ DLGPROC DlgProc
442  )
443 {
444  HPROPSHEETPAGE propSheetPageHandle;
445  PROPSHEETPAGE propSheetPage;
446 
447  memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
448  propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
449  propSheetPage.dwFlags = PSP_USECALLBACK;
450  propSheetPage.hInstance = PluginInstance->DllBase;
451  propSheetPage.pszTemplate = Template;
452  propSheetPage.pfnDlgProc = DlgProc;
453  propSheetPage.lParam = (LPARAM)Context;
454  propSheetPage.pfnCallback = WepCommonPropPageProc;
455 
456  propSheetPageHandle = CreatePropertySheetPage(&propSheetPage);
457 
458  return propSheetPageHandle;
459 }
460 
461 static INT CALLBACK WepCommonPropPageProc(
462  _In_ HWND hwnd,
463  _In_ UINT uMsg,
464  _In_ LPPROPSHEETPAGE ppsp
465  )
466 {
468 
469  context = (PWINDOW_PROPERTIES_CONTEXT)ppsp->lParam;
470 
471  if (uMsg == PSPCB_ADDREF)
473  else if (uMsg == PSPCB_RELEASE)
475 
476  return 1;
477 }
478 
480  _In_ PVOID Parameter
481  )
482 {
483  PH_AUTO_POOL autoPool;
484  BOOL result;
485  MSG message;
486  BOOLEAN processed;
487  ULONG i;
488 
489  PhInitializeAutoPool(&autoPool);
490 
491  WePropertiesThreadClientId = NtCurrentTeb()->ClientId;
492 
493  // Force the creation of the message queue so PostThreadMessage works.
494  PeekMessage(&message, NULL, WM_USER, WM_USER, PM_NOREMOVE);
495  PhSetEvent(&WePropertiesThreadReadyEvent);
496 
497  while (result = GetMessage(&message, NULL, 0, 0))
498  {
499  if (result == -1)
500  break;
501 
502  if (WePropertiesCreateList->Count != 0)
503  {
504  PhAcquireQueuedLockExclusive(&WePropertiesCreateLock);
505 
506  for (i = 0; i < WePropertiesCreateList->Count; i++)
507  {
509  HWND hwnd;
510 
511  context = WePropertiesCreateList->Items[i];
512  hwnd = WepCreateWindowProperties(context);
514  PhAddItemList(WePropertiesWindowList, hwnd);
515  }
516 
517  PhClearList(WePropertiesCreateList);
518  PhReleaseQueuedLockExclusive(&WePropertiesCreateLock);
519  }
520 
521  processed = FALSE;
522 
523  for (i = 0; i < WePropertiesWindowList->Count; i++)
524  {
525  if (PropSheet_IsDialogMessage(WePropertiesWindowList->Items[i], &message))
526  {
527  processed = TRUE;
528  break;
529  }
530  }
531 
532  if (!processed)
533  {
534  TranslateMessage(&message);
535  DispatchMessage(&message);
536  }
537 
538  // Destroy properties windows when necessary.
539  for (i = 0; i < WePropertiesWindowList->Count; i++)
540  {
541  if (!PropSheet_GetCurrentPageHwnd(WePropertiesWindowList->Items[i]))
542  {
543  DestroyWindow(WePropertiesWindowList->Items[i]);
544  PhRemoveItemList(WePropertiesWindowList, i);
545  i--;
546  }
547  }
548 
549  PhDrainAutoPool(&autoPool);
550  }
551 
552  PhDeleteAutoPool(&autoPool);
553 
554  return STATUS_SUCCESS;
555 }
556 
557 FORCEINLINE BOOLEAN WepPropPageDlgProcHeader(
558  _In_ HWND hwndDlg,
559  _In_ UINT uMsg,
560  _In_ LPARAM lParam,
561  _Out_opt_ LPPROPSHEETPAGE *PropSheetPage,
562  _Out_opt_ PWINDOW_PROPERTIES_CONTEXT *Context
563  )
564 {
565  LPPROPSHEETPAGE propSheetPage;
566 
567  if (uMsg == WM_INITDIALOG)
568  {
569  propSheetPage = (LPPROPSHEETPAGE)lParam;
570  // Save the context.
571  SetProp(hwndDlg, L"PropSheetPage", (HANDLE)lParam);
572  }
573  else
574  {
575  propSheetPage = (LPPROPSHEETPAGE)GetProp(hwndDlg, L"PropSheetPage");
576 
577  if (uMsg == WM_DESTROY)
578  RemoveProp(hwndDlg, L"PropSheetPage");
579  }
580 
581  if (!propSheetPage)
582  return FALSE;
583 
584  if (PropSheetPage)
585  *PropSheetPage = propSheetPage;
586  if (Context)
587  *Context = (PWINDOW_PROPERTIES_CONTEXT)propSheetPage->lParam;
588 
589  return TRUE;
590 }
591 
592 static VOID WepEnsureHookDataValid(
593  _In_ PWINDOW_PROPERTIES_CONTEXT Context
594  )
595 {
596  if (!Context->HookDataValid)
597  {
599 #ifdef _WIN64
600  HANDLE processHandle;
601  BOOLEAN isWow64 = FALSE;
602 #endif
603 
604  // The desktop window is owned by CSR. The hook will never work on the desktop window.
605  if (Context->WindowHandle == GetDesktopWindow())
606  {
607  Context->HookDataValid = TRUE;
608  return;
609  }
610 
611 #ifdef _WIN64
612  // We can't use the hook on WOW64 processes.
613  if (NT_SUCCESS(PhOpenProcess(&processHandle, *(PULONG)WeGetProcedureAddress("ProcessQueryAccess"), Context->ClientId.UniqueProcess)))
614  {
615  PhGetProcessIsWow64(processHandle, &isWow64);
616  NtClose(processHandle);
617  }
618 
619  if (isWow64)
620  return;
621 #endif
622 
624 
625  Context->HookDataSuccess = FALSE;
626 
627  if (WeLockServerSharedData(&data))
628  {
629  if (WeSendServerRequest(Context->WindowHandle))
630  {
631  Context->WndProc = data->c.WndProc;
632  Context->DlgProc = data->c.DlgProc;
633  memcpy(&Context->ClassInfo, &data->c.ClassInfo, sizeof(WNDCLASSEX));
634  Context->HookDataSuccess = TRUE;
635  }
636 
638  }
639 
640  Context->HookDataValid = TRUE;
641  }
642 }
643 
644 static BOOLEAN NTAPI EnumGenericModulesCallback(
645  _In_ PPH_MODULE_INFO Module,
646  _In_opt_ PVOID Context
647  )
648 {
649  PWINDOW_PROPERTIES_CONTEXT context = Context;
650 
651  PhLoadModuleSymbolProvider(context->SymbolProvider, Module->FileName->Buffer,
652  (ULONG64)Module->BaseAddress, Module->Size);
653 
654  return TRUE;
655 }
656 
657 static NTSTATUS WepResolveSymbolFunction(
658  _In_ PVOID Parameter
659  )
660 {
661  PSYMBOL_RESOLVE_CONTEXT context = Parameter;
662 
663  if (PhBeginInitOnce(&context->Context->SymbolProviderInitOnce))
664  {
665  PhEnumGenericModules(context->Context->ClientId.UniqueProcess, NULL, 0, EnumGenericModulesCallback, context->Context);
666  PhEndInitOnce(&context->Context->SymbolProviderInitOnce);
667  }
668 
669  context->Symbol = PhGetSymbolFromAddress(
670  context->Context->SymbolProvider,
671  (ULONG64)context->Address,
672  &context->ResolveLevel,
673  NULL,
674  NULL,
675  NULL
676  );
677 
678  // Fail if we don't have a symbol.
679  if (!context->Symbol)
680  {
681  WepDereferenceWindowPropertiesContext(context->Context);
682  PhFree(context);
683  return STATUS_SUCCESS;
684  }
685 
686  PhAcquireQueuedLockExclusive(&context->Context->ResolveListLock);
687  InsertHeadList(&context->Context->ResolveListHead, &context->ListEntry);
688  PhReleaseQueuedLockExclusive(&context->Context->ResolveListLock);
689 
690  PostMessage(context->NotifyWindow, WEM_RESOLVE_DONE, 0, (LPARAM)context);
691 
692  WepDereferenceWindowPropertiesContext(context->Context);
693 
694  return STATUS_SUCCESS;
695 }
696 
697 static VOID WepQueueResolveSymbol(
698  _In_ PWINDOW_PROPERTIES_CONTEXT Context,
699  _In_ HWND NotifyWindow,
700  _In_ ULONG64 Address,
701  _In_ ULONG Id
702  )
703 {
704  PSYMBOL_RESOLVE_CONTEXT resolveContext;
705 
706  if (!Context->SymbolProvider)
707  {
708  Context->SymbolProvider = PhCreateSymbolProvider(Context->ClientId.UniqueProcess);
709  PhLoadSymbolProviderOptions(Context->SymbolProvider);
710  }
711 
712  resolveContext = PhAllocate(sizeof(SYMBOL_RESOLVE_CONTEXT));
713  resolveContext->Address = Address;
714  resolveContext->Symbol = NULL;
715  resolveContext->ResolveLevel = PhsrlInvalid;
716  resolveContext->NotifyWindow = NotifyWindow;
717  resolveContext->Context = Context;
719  resolveContext->Id = Id;
720 
721  PhQueueItemGlobalWorkQueue(WepResolveSymbolFunction, resolveContext);
722 }
723 
724 static PPH_STRING WepFormatRect(
725  _In_ PRECT Rect
726  )
727 {
728  return PhaFormatString(L"(%d, %d) - (%d, %d) [%dx%d]",
729  Rect->left, Rect->top, Rect->right, Rect->bottom,
730  Rect->right - Rect->left, Rect->bottom - Rect->top);
731 }
732 
733 static VOID WepRefreshWindowGeneralInfoSymbols(
734  _In_ HWND hwndDlg,
735  _In_ PWINDOW_PROPERTIES_CONTEXT Context
736  )
737 {
738  if (Context->WndProcResolving != 0)
739  SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->WndProc)->Buffer);
740  else if (Context->WndProcSymbol)
741  SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (%s)", Context->WndProc, Context->WndProcSymbol->Buffer)->Buffer);
742  else if (Context->WndProc != 0)
743  SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix", Context->WndProc)->Buffer);
744  else
745  SetDlgItemText(hwndDlg, IDC_WINDOWPROC, L"Unknown");
746 
747  if (Context->DlgProcResolving != 0)
748  SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->DlgProc)->Buffer);
749  else if (Context->DlgProcSymbol)
750  SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix (%s)", Context->DlgProc, Context->DlgProcSymbol->Buffer)->Buffer);
751  else if (Context->DlgProc != 0)
752  SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix", Context->DlgProc)->Buffer);
753  else if (Context->WndProc != 0)
754  SetDlgItemText(hwndDlg, IDC_DIALOGPROC, L"N/A");
755  else
756  SetDlgItemText(hwndDlg, IDC_DIALOGPROC, L"Unknown");
757 }
758 
759 static VOID WepRefreshWindowGeneralInfo(
760  _In_ HWND hwndDlg,
761  _In_ PWINDOW_PROPERTIES_CONTEXT Context
762  )
763 {
764  PPH_STRING windowText;
765  PPH_STRING clientIdName;
766  WINDOWINFO windowInfo = { sizeof(WINDOWINFO) };
767  WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT) };
768  MONITORINFO monitorInfo = { sizeof(MONITORINFO) };
769 
770  clientIdName = PhGetClientIdName(&Context->ClientId);
771  SetDlgItemText(hwndDlg, IDC_THREAD, clientIdName->Buffer);
772  PhDereferenceObject(clientIdName);
773 
774  windowText = PhAutoDereferenceObject(PhGetWindowText(Context->WindowHandle));
775  SetDlgItemText(hwndDlg, IDC_TEXT, PhGetStringOrEmpty(windowText));
776 
777  if (GetWindowInfo(Context->WindowHandle, &windowInfo))
778  {
779  SetDlgItemText(hwndDlg, IDC_RECTANGLE, WepFormatRect(&windowInfo.rcWindow)->Buffer);
780  SetDlgItemText(hwndDlg, IDC_CLIENTRECTANGLE, WepFormatRect(&windowInfo.rcClient)->Buffer);
781  }
782  else
783  {
784  SetDlgItemText(hwndDlg, IDC_RECTANGLE, L"N/A");
785  SetDlgItemText(hwndDlg, IDC_CLIENTRECTANGLE, L"N/A");
786  }
787 
788  if (GetWindowPlacement(Context->WindowHandle, &windowPlacement))
789  {
790  // The rectangle is in workspace coordinates. Convert the values back to screen coordinates.
791  if (GetMonitorInfo(MonitorFromRect(&windowPlacement.rcNormalPosition, MONITOR_DEFAULTTOPRIMARY), &monitorInfo))
792  {
793  windowPlacement.rcNormalPosition.left += monitorInfo.rcWork.left;
794  windowPlacement.rcNormalPosition.top += monitorInfo.rcWork.top;
795  windowPlacement.rcNormalPosition.right += monitorInfo.rcWork.left;
796  windowPlacement.rcNormalPosition.bottom += monitorInfo.rcWork.top;
797  }
798 
799  SetDlgItemText(hwndDlg, IDC_NORMALRECTANGLE, WepFormatRect(&windowPlacement.rcNormalPosition)->Buffer);
800  }
801  else
802  {
803  SetDlgItemText(hwndDlg, IDC_NORMALRECTANGLE, L"N/A");
804  }
805 
806  SetDlgItemText(hwndDlg, IDC_INSTANCEHANDLE, PhaFormatString(L"0x%Ix", GetWindowLongPtr(Context->WindowHandle, GWLP_HINSTANCE))->Buffer);
807  SetDlgItemText(hwndDlg, IDC_MENUHANDLE, PhaFormatString(L"0x%Ix", GetMenu(Context->WindowHandle))->Buffer);
808  SetDlgItemText(hwndDlg, IDC_USERDATA, PhaFormatString(L"0x%Ix", GetWindowLongPtr(Context->WindowHandle, GWLP_USERDATA))->Buffer);
809  SetDlgItemText(hwndDlg, IDC_UNICODE, IsWindowUnicode(Context->WindowHandle) ? L"Yes" : L"No");
810 
811  WepEnsureHookDataValid(Context);
812 
813  if (Context->WndProc != 0)
814  {
815  Context->WndProcResolving++;
816  WepQueueResolveSymbol(Context, hwndDlg, Context->WndProc, 1);
817  }
818 
819  if (Context->DlgProc != 0)
820  {
821  Context->DlgProcResolving++;
822  WepQueueResolveSymbol(Context, hwndDlg, Context->DlgProc, 2);
823  }
824 
825  WepRefreshWindowGeneralInfoSymbols(hwndDlg, Context);
826 }
827 
828 INT_PTR CALLBACK WepWindowGeneralDlgProc(
829  _In_ HWND hwndDlg,
830  _In_ UINT uMsg,
831  _In_ WPARAM wParam,
832  _In_ LPARAM lParam
833  )
834 {
836 
837  if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context))
838  return FALSE;
839 
840  switch (uMsg)
841  {
842  case WM_INITDIALOG:
843  {
844  WepRefreshWindowGeneralInfo(hwndDlg, context);
845  }
846  break;
847  case WM_COMMAND:
848  {
849  switch (LOWORD(wParam))
850  {
851  case IDC_REFRESH:
852  context->HookDataValid = FALSE;
853  PhClearReference(&context->WndProcSymbol);
854  WepRefreshWindowGeneralInfo(hwndDlg, context);
855  break;
856  }
857  }
858  break;
859  case WEM_RESOLVE_DONE:
860  {
861  PSYMBOL_RESOLVE_CONTEXT resolveContext = (PSYMBOL_RESOLVE_CONTEXT)lParam;
862 
863  if (resolveContext->Id == 1)
864  {
865  PhAcquireQueuedLockExclusive(&context->ResolveListLock);
866  RemoveEntryList(&resolveContext->ListEntry);
867  PhReleaseQueuedLockExclusive(&context->ResolveListLock);
868 
869  if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction)
870  PhClearReference(&resolveContext->Symbol);
871 
872  PhMoveReference(&context->WndProcSymbol, resolveContext->Symbol);
873  PhFree(resolveContext);
874 
875  context->WndProcResolving--;
876  }
877  else if (resolveContext->Id == 2)
878  {
879  PhAcquireQueuedLockExclusive(&context->ResolveListLock);
880  RemoveEntryList(&resolveContext->ListEntry);
881  PhReleaseQueuedLockExclusive(&context->ResolveListLock);
882 
883  if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction)
884  PhClearReference(&resolveContext->Symbol);
885 
886  PhMoveReference(&context->DlgProcSymbol, resolveContext->Symbol);
887  PhFree(resolveContext);
888 
889  context->DlgProcResolving--;
890  }
891 
892  WepRefreshWindowGeneralInfoSymbols(hwndDlg, context);
893  }
894  break;
895  }
896 
897  return FALSE;
898 }
899 
900 static VOID WepRefreshWindowStyles(
901  _In_ HWND hwndDlg,
902  _In_ PWINDOW_PROPERTIES_CONTEXT Context
903  )
904 {
905  WINDOWINFO windowInfo = { sizeof(WINDOWINFO) };
906  HWND stylesListBox;
907  HWND extendedStylesListBox;
908  ULONG i;
909 
910  stylesListBox = GetDlgItem(hwndDlg, IDC_STYLESLIST);
911  extendedStylesListBox = GetDlgItem(hwndDlg, IDC_EXTENDEDSTYLESLIST);
912 
913  ListBox_ResetContent(stylesListBox);
914  ListBox_ResetContent(extendedStylesListBox);
915 
916  if (GetWindowInfo(Context->WindowHandle, &windowInfo))
917  {
918  SetDlgItemText(hwndDlg, IDC_STYLES, PhaFormatString(L"0x%x", windowInfo.dwStyle)->Buffer);
919  SetDlgItemText(hwndDlg, IDC_EXTENDEDSTYLES, PhaFormatString(L"0x%x", windowInfo.dwExStyle)->Buffer);
920 
921  for (i = 0; i < sizeof(WepStylePairs) / sizeof(STRING_INTEGER_PAIR); i++)
922  {
923  if (windowInfo.dwStyle & WepStylePairs[i].Integer)
924  {
925  // Skip irrelevant styles.
926 
927  if (WepStylePairs[i].Integer == WS_MAXIMIZEBOX ||
928  WepStylePairs[i].Integer == WS_MINIMIZEBOX)
929  {
930  if (windowInfo.dwStyle & WS_CHILD)
931  continue;
932  }
933 
934  if (WepStylePairs[i].Integer == WS_TABSTOP ||
935  WepStylePairs[i].Integer == WS_GROUP)
936  {
937  if (!(windowInfo.dwStyle & WS_CHILD))
938  continue;
939  }
940 
941  ListBox_AddString(stylesListBox, WepStylePairs[i].String);
942  }
943  }
944 
945  for (i = 0; i < sizeof(WepExtendedStylePairs) / sizeof(STRING_INTEGER_PAIR); i++)
946  {
947  if (windowInfo.dwExStyle & WepExtendedStylePairs[i].Integer)
948  {
949  ListBox_AddString(extendedStylesListBox, WepExtendedStylePairs[i].String);
950  }
951  }
952  }
953  else
954  {
955  SetDlgItemText(hwndDlg, IDC_STYLES, L"N/A");
956  SetDlgItemText(hwndDlg, IDC_EXTENDEDSTYLES, L"N/A");
957  }
958 }
959 
960 INT_PTR CALLBACK WepWindowStylesDlgProc(
961  _In_ HWND hwndDlg,
962  _In_ UINT uMsg,
963  _In_ WPARAM wParam,
964  _In_ LPARAM lParam
965  )
966 {
968 
969  if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context))
970  return FALSE;
971 
972  switch (uMsg)
973  {
974  case WM_INITDIALOG:
975  {
976  WepRefreshWindowStyles(hwndDlg, context);
977  }
978  break;
979  case WM_COMMAND:
980  {
981  switch (LOWORD(wParam))
982  {
983  case IDC_REFRESH:
984  WepRefreshWindowStyles(hwndDlg, context);
985  break;
986  }
987  }
988  break;
989  }
990 
991  return FALSE;
992 }
993 
994 static VOID WepRefreshWindowClassInfoSymbols(
995  _In_ HWND hwndDlg,
996  _In_ PWINDOW_PROPERTIES_CONTEXT Context
997  )
998 {
999  if (Context->ClassWndProcResolving != 0)
1000  SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->ClassInfo.lpfnWndProc)->Buffer);
1001  else if (Context->ClassWndProcSymbol)
1002  SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (%s)", Context->ClassInfo.lpfnWndProc, Context->ClassWndProcSymbol->Buffer)->Buffer);
1003  else if (Context->ClassInfo.lpfnWndProc)
1004  SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix", Context->ClassInfo.lpfnWndProc)->Buffer);
1005  else
1006  SetDlgItemText(hwndDlg, IDC_WINDOWPROC, L"Unknown");
1007 }
1008 
1009 static VOID WepRefreshWindowClassInfo(
1010  _In_ HWND hwndDlg,
1011  _In_ PWINDOW_PROPERTIES_CONTEXT Context
1012  )
1013 {
1014  WCHAR className[256];
1015  PH_STRING_BUILDER stringBuilder;
1016  ULONG i;
1017 
1018  if (!GetClassName(Context->WindowHandle, className, sizeof(className) / sizeof(WCHAR)))
1019  className[0] = 0;
1020 
1021  WepEnsureHookDataValid(Context);
1022 
1023  if (!Context->HookDataSuccess)
1024  {
1025  Context->ClassInfo.cbSize = sizeof(WNDCLASSEX);
1026  GetClassInfoEx(NULL, className, &Context->ClassInfo);
1027  }
1028 
1029  SetDlgItemText(hwndDlg, IDC_NAME, className);
1030  SetDlgItemText(hwndDlg, IDC_ATOM, PhaFormatString(L"0x%x", GetClassWord(Context->WindowHandle, GCW_ATOM))->Buffer);
1031  SetDlgItemText(hwndDlg, IDC_INSTANCEHANDLE, PhaFormatString(L"0x%Ix", GetClassLongPtr(Context->WindowHandle, GCLP_HMODULE))->Buffer);
1032  SetDlgItemText(hwndDlg, IDC_ICONHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hIcon)->Buffer);
1033  SetDlgItemText(hwndDlg, IDC_SMALLICONHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hIconSm)->Buffer);
1034  SetDlgItemText(hwndDlg, IDC_MENUNAME, PhaFormatString(L"0x%Ix", Context->ClassInfo.lpszMenuName)->Buffer);
1035 
1036  PhInitializeStringBuilder(&stringBuilder, 100);
1037  PhAppendFormatStringBuilder(&stringBuilder, L"0x%x (", Context->ClassInfo.style);
1038 
1039  for (i = 0; i < sizeof(WepClassStylePairs) / sizeof(STRING_INTEGER_PAIR); i++)
1040  {
1041  if (Context->ClassInfo.style & WepClassStylePairs[i].Integer)
1042  {
1043  PhAppendStringBuilder2(&stringBuilder, WepClassStylePairs[i].String);
1044  PhAppendStringBuilder2(&stringBuilder, L" | ");
1045  }
1046  }
1047 
1048  if (PhEndsWithString2(stringBuilder.String, L" | ", FALSE))
1049  {
1050  PhRemoveEndStringBuilder(&stringBuilder, 3);
1051  PhAppendCharStringBuilder(&stringBuilder, ')');
1052  }
1053  else
1054  {
1055  // No styles. Remove the brackets.
1056  PhRemoveEndStringBuilder(&stringBuilder, 1);
1057  }
1058 
1059  SetDlgItemText(hwndDlg, IDC_STYLES, stringBuilder.String->Buffer);
1060  PhDeleteStringBuilder(&stringBuilder);
1061 
1062  // TODO: Add symbols for these values.
1063  SetDlgItemText(hwndDlg, IDC_CURSORHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hCursor)->Buffer);
1064  SetDlgItemText(hwndDlg, IDC_BACKGROUNDBRUSH, PhaFormatString(L"0x%Ix", Context->ClassInfo.hbrBackground)->Buffer);
1065 
1066  if (Context->ClassInfo.lpfnWndProc)
1067  {
1068  Context->ClassWndProcResolving++;
1069  WepQueueResolveSymbol(Context, hwndDlg, (ULONG_PTR)Context->ClassInfo.lpfnWndProc, 0);
1070  }
1071 
1072  WepRefreshWindowClassInfoSymbols(hwndDlg, Context);
1073 }
1074 
1075 INT_PTR CALLBACK WepWindowClassDlgProc(
1076  _In_ HWND hwndDlg,
1077  _In_ UINT uMsg,
1078  _In_ WPARAM wParam,
1079  _In_ LPARAM lParam
1080  )
1081 {
1083 
1084  if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context))
1085  return FALSE;
1086 
1087  switch (uMsg)
1088  {
1089  case WM_INITDIALOG:
1090  {
1091  WepRefreshWindowClassInfo(hwndDlg, context);
1092  }
1093  break;
1094  case WM_COMMAND:
1095  {
1096  switch (LOWORD(wParam))
1097  {
1098  case IDC_REFRESH:
1099  context->HookDataValid = FALSE;
1100  PhClearReference(&context->ClassWndProcSymbol);
1101  WepRefreshWindowClassInfo(hwndDlg, context);
1102  break;
1103  }
1104  }
1105  break;
1106  case WEM_RESOLVE_DONE:
1107  {
1108  PSYMBOL_RESOLVE_CONTEXT resolveContext = (PSYMBOL_RESOLVE_CONTEXT)lParam;
1109 
1110  PhAcquireQueuedLockExclusive(&context->ResolveListLock);
1111  RemoveEntryList(&resolveContext->ListEntry);
1112  PhReleaseQueuedLockExclusive(&context->ResolveListLock);
1113 
1114  if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction)
1115  PhClearReference(&resolveContext->Symbol);
1116 
1117  PhMoveReference(&context->ClassWndProcSymbol, resolveContext->Symbol);
1118  PhFree(resolveContext);
1119 
1120  context->ClassWndProcResolving--;
1121  WepRefreshWindowClassInfoSymbols(hwndDlg, context);
1122  }
1123  break;
1124  }
1125 
1126  return FALSE;
1127 }
1128 
1129 static BOOL CALLBACK EnumPropsExCallback(
1130  _In_ HWND hwnd,
1131  _In_ LPTSTR lpszString,
1132  _In_ HANDLE hData,
1133  _In_ ULONG_PTR dwData
1134  )
1135 {
1136  INT lvItemIndex;
1137  PWSTR propName;
1138  WCHAR value[PH_PTR_STR_LEN_1];
1139 
1140  propName = lpszString;
1141 
1142  if ((ULONG_PTR)lpszString < USHRT_MAX)
1143  {
1144  // This is an integer atom.
1145  propName = PhaFormatString(L"#%lu", (ULONG)lpszString)->Buffer;
1146  }
1147 
1148  lvItemIndex = PhAddListViewItem((HWND)dwData, MAXINT, propName, NULL);
1149 
1150  PhPrintPointer(value, (PVOID)hData);
1151  PhSetListViewSubItem((HWND)dwData, lvItemIndex, 1, value);
1152 
1153  return TRUE;
1154 }
1155 
1156 static VOID WepRefreshWindowProps(
1157  _In_ HWND hwndDlg,
1158  _In_ HWND ListViewHandle,
1159  _In_ PWINDOW_PROPERTIES_CONTEXT Context
1160  )
1161 {
1162  ExtendedListView_SetRedraw(ListViewHandle, FALSE);
1163  ListView_DeleteAllItems(ListViewHandle);
1164  EnumPropsEx(Context->WindowHandle, EnumPropsExCallback, (LPARAM)ListViewHandle);
1165  ExtendedListView_SortItems(ListViewHandle);
1166  ExtendedListView_SetRedraw(ListViewHandle, TRUE);
1167 }
1168 
1170  _In_ HWND hwndDlg,
1171  _In_ UINT uMsg,
1172  _In_ WPARAM wParam,
1173  _In_ LPARAM lParam
1174  )
1175 {
1177 
1178  if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context))
1179  return FALSE;
1180 
1181  switch (uMsg)
1182  {
1183  case WM_INITDIALOG:
1184  {
1185  HWND lvHandle;
1186 
1187  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
1188  PhSetListViewStyle(lvHandle, FALSE, TRUE);
1189  PhSetControlTheme(lvHandle, L"explorer");
1190 
1191  PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 160, L"Name");
1192  PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Value");
1193  PhSetExtendedListView(lvHandle);
1194 
1195  WepRefreshWindowProps(hwndDlg, lvHandle, context);
1196  }
1197  break;
1198  case WM_COMMAND:
1199  {
1200  switch (LOWORD(wParam))
1201  {
1202  case IDC_REFRESH:
1203  WepRefreshWindowProps(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), context);
1204  break;
1205  }
1206  }
1207  break;
1208  }
1209 
1210  return FALSE;
1211 }