/* * Process Hacker Window Explorer - * window properties * * Copyright (C) 2011 wj32 * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Process Hacker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . */ // Since the main message loop doesn't support property sheets, we need a separate thread to run // property sheets on. #include "wndexp.h" #include "resource.h" #include #include #include #define NUMBER_OF_PAGES 4 #define WEM_RESOLVE_DONE (WM_APP + 1234) typedef struct _WINDOW_PROPERTIES_CONTEXT { LONG RefCount; HWND ParentWindowHandle; HWND WindowHandle; CLIENT_ID ClientId; PH_INITONCE SymbolProviderInitOnce; PPH_SYMBOL_PROVIDER SymbolProvider; LIST_ENTRY ResolveListHead; PH_QUEUED_LOCK ResolveListLock; PPH_STRING WndProcSymbol; ULONG WndProcResolving; PPH_STRING DlgProcSymbol; ULONG DlgProcResolving; PPH_STRING ClassWndProcSymbol; ULONG ClassWndProcResolving; BOOLEAN HookDataValid; BOOLEAN HookDataSuccess; ULONG_PTR WndProc; ULONG_PTR DlgProc; WNDCLASSEX ClassInfo; } WINDOW_PROPERTIES_CONTEXT, *PWINDOW_PROPERTIES_CONTEXT; typedef struct _SYMBOL_RESOLVE_CONTEXT { LIST_ENTRY ListEntry; ULONG64 Address; PPH_STRING Symbol; PH_SYMBOL_RESOLVE_LEVEL ResolveLevel; HWND NotifyWindow; PWINDOW_PROPERTIES_CONTEXT Context; ULONG Id; } SYMBOL_RESOLVE_CONTEXT, *PSYMBOL_RESOLVE_CONTEXT; typedef struct _STRING_INTEGER_PAIR { PWSTR String; ULONG Integer; } STRING_INTEGER_PAIR, *PSTRING_INTEGER_PAIR; VOID WepReferenceWindowPropertiesContext( _Inout_ PWINDOW_PROPERTIES_CONTEXT Context ); VOID WepDereferenceWindowPropertiesContext( _Inout_ PWINDOW_PROPERTIES_CONTEXT Context ); HWND WepCreateWindowProperties( _In_ PWINDOW_PROPERTIES_CONTEXT Context ); INT CALLBACK WepPropSheetProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ LPARAM lParam ); LRESULT CALLBACK WepPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ); HPROPSHEETPAGE WepCommonCreatePage( _In_ PWINDOW_PROPERTIES_CONTEXT Context, _In_ PWSTR Template, _In_ DLGPROC DlgProc ); INT CALLBACK WepCommonPropPageProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ LPPROPSHEETPAGE ppsp ); NTSTATUS WepPropertiesThreadStart( _In_ PVOID Parameter ); INT_PTR CALLBACK WepWindowGeneralDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ); INT_PTR CALLBACK WepWindowStylesDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ); INT_PTR CALLBACK WepWindowClassDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ); INT_PTR CALLBACK WepWindowPropertiesDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ); #define DEFINE_PAIR(Symbol) { L#Symbol, Symbol } static STRING_INTEGER_PAIR WepStylePairs[] = { DEFINE_PAIR(WS_POPUP), DEFINE_PAIR(WS_CHILD), DEFINE_PAIR(WS_MINIMIZE), DEFINE_PAIR(WS_VISIBLE), DEFINE_PAIR(WS_DISABLED), DEFINE_PAIR(WS_CLIPSIBLINGS), DEFINE_PAIR(WS_CLIPCHILDREN), DEFINE_PAIR(WS_MAXIMIZE), DEFINE_PAIR(WS_BORDER), DEFINE_PAIR(WS_DLGFRAME), DEFINE_PAIR(WS_VSCROLL), DEFINE_PAIR(WS_HSCROLL), DEFINE_PAIR(WS_SYSMENU), DEFINE_PAIR(WS_THICKFRAME), DEFINE_PAIR(WS_GROUP), DEFINE_PAIR(WS_TABSTOP), DEFINE_PAIR(WS_MINIMIZEBOX), DEFINE_PAIR(WS_MAXIMIZEBOX) }; static STRING_INTEGER_PAIR WepExtendedStylePairs[] = { DEFINE_PAIR(WS_EX_DLGMODALFRAME), DEFINE_PAIR(WS_EX_NOPARENTNOTIFY), DEFINE_PAIR(WS_EX_TOPMOST), DEFINE_PAIR(WS_EX_ACCEPTFILES), DEFINE_PAIR(WS_EX_TRANSPARENT), DEFINE_PAIR(WS_EX_MDICHILD), DEFINE_PAIR(WS_EX_TOOLWINDOW), DEFINE_PAIR(WS_EX_WINDOWEDGE), DEFINE_PAIR(WS_EX_CLIENTEDGE), DEFINE_PAIR(WS_EX_CONTEXTHELP), DEFINE_PAIR(WS_EX_RIGHT), DEFINE_PAIR(WS_EX_RTLREADING), DEFINE_PAIR(WS_EX_LEFTSCROLLBAR), DEFINE_PAIR(WS_EX_CONTROLPARENT), DEFINE_PAIR(WS_EX_STATICEDGE), DEFINE_PAIR(WS_EX_APPWINDOW), DEFINE_PAIR(WS_EX_LAYERED), DEFINE_PAIR(WS_EX_NOINHERITLAYOUT), DEFINE_PAIR(WS_EX_LAYOUTRTL), DEFINE_PAIR(WS_EX_COMPOSITED), DEFINE_PAIR(WS_EX_NOACTIVATE) }; static STRING_INTEGER_PAIR WepClassStylePairs[] = { DEFINE_PAIR(CS_VREDRAW), DEFINE_PAIR(CS_HREDRAW), DEFINE_PAIR(CS_DBLCLKS), DEFINE_PAIR(CS_OWNDC), DEFINE_PAIR(CS_CLASSDC), DEFINE_PAIR(CS_PARENTDC), DEFINE_PAIR(CS_NOCLOSE), DEFINE_PAIR(CS_SAVEBITS), DEFINE_PAIR(CS_BYTEALIGNCLIENT), DEFINE_PAIR(CS_BYTEALIGNWINDOW), DEFINE_PAIR(CS_GLOBALCLASS), DEFINE_PAIR(CS_IME), DEFINE_PAIR(CS_DROPSHADOW) }; HANDLE WePropertiesThreadHandle = NULL; CLIENT_ID WePropertiesThreadClientId; PH_EVENT WePropertiesThreadReadyEvent = PH_EVENT_INIT; PPH_LIST WePropertiesCreateList; PPH_LIST WePropertiesWindowList; PH_QUEUED_LOCK WePropertiesCreateLock = PH_QUEUED_LOCK_INIT; VOID WeShowWindowProperties( _In_ HWND ParentWindowHandle, _In_ HWND WindowHandle ) { PWINDOW_PROPERTIES_CONTEXT context; ULONG threadId; ULONG processId; if (!WePropertiesCreateList) WePropertiesCreateList = PhCreateList(4); if (!WePropertiesWindowList) WePropertiesWindowList = PhCreateList(4); if (!WePropertiesThreadHandle) { WePropertiesThreadHandle = PhCreateThread(0, WepPropertiesThreadStart, NULL); PhWaitForEvent(&WePropertiesThreadReadyEvent, NULL); } context = PhAllocate(sizeof(WINDOW_PROPERTIES_CONTEXT)); memset(context, 0, sizeof(WINDOW_PROPERTIES_CONTEXT)); context->RefCount = 1; context->ParentWindowHandle = ParentWindowHandle; context->WindowHandle = WindowHandle; processId = 0; threadId = GetWindowThreadProcessId(WindowHandle, &processId); context->ClientId.UniqueProcess = UlongToHandle(processId); context->ClientId.UniqueThread = UlongToHandle(threadId); PhInitializeInitOnce(&context->SymbolProviderInitOnce); InitializeListHead(&context->ResolveListHead); PhInitializeQueuedLock(&context->ResolveListLock); // Queue the window for creation and wake up the host thread. PhAcquireQueuedLockExclusive(&WePropertiesCreateLock); PhAddItemList(WePropertiesCreateList, context); PhReleaseQueuedLockExclusive(&WePropertiesCreateLock); PostThreadMessage(HandleToUlong(WePropertiesThreadClientId.UniqueThread), WM_NULL, 0, 0); } VOID WepReferenceWindowPropertiesContext( _Inout_ PWINDOW_PROPERTIES_CONTEXT Context ) { _InterlockedIncrement(&Context->RefCount); } VOID WepDereferenceWindowPropertiesContext( _Inout_ PWINDOW_PROPERTIES_CONTEXT Context ) { if (_InterlockedDecrement(&Context->RefCount) == 0) { PLIST_ENTRY listEntry; PhClearReference(&Context->SymbolProvider); // Destroy results that have not been processed by any property pages. listEntry = Context->ResolveListHead.Flink; while (listEntry != &Context->ResolveListHead) { PSYMBOL_RESOLVE_CONTEXT resolveContext; resolveContext = CONTAINING_RECORD(listEntry, SYMBOL_RESOLVE_CONTEXT, ListEntry); listEntry = listEntry->Flink; PhClearReference(&resolveContext->Symbol); PhFree(resolveContext); } PhClearReference(&Context->WndProcSymbol); PhClearReference(&Context->DlgProcSymbol); PhClearReference(&Context->ClassWndProcSymbol); PhFree(Context); } } static HWND WepCreateWindowProperties( _In_ PWINDOW_PROPERTIES_CONTEXT Context ) { PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; HPROPSHEETPAGE pages[NUMBER_OF_PAGES]; propSheetHeader.dwFlags = PSH_MODELESS | PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE | PSH_USECALLBACK; propSheetHeader.hwndParent = Context->ParentWindowHandle; propSheetHeader.pszCaption = PhaFormatString(L"Window %Ix", Context->WindowHandle)->Buffer; propSheetHeader.nPages = 0; propSheetHeader.nStartPage = 0; propSheetHeader.phpage = pages; propSheetHeader.pfnCallback = WepPropSheetProc; // General pages[propSheetHeader.nPages++] = WepCommonCreatePage( Context, MAKEINTRESOURCE(IDD_WNDGENERAL), WepWindowGeneralDlgProc ); // Styles pages[propSheetHeader.nPages++] = WepCommonCreatePage( Context, MAKEINTRESOURCE(IDD_WNDSTYLES), WepWindowStylesDlgProc ); // Class pages[propSheetHeader.nPages++] = WepCommonCreatePage( Context, MAKEINTRESOURCE(IDD_WNDCLASS), WepWindowClassDlgProc ); // Properties pages[propSheetHeader.nPages++] = WepCommonCreatePage( Context, MAKEINTRESOURCE(IDD_WNDPROPS), WepWindowPropertiesDlgProc ); return (HWND)PropertySheet(&propSheetHeader); } static INT CALLBACK WepPropSheetProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ LPARAM lParam ) { switch (uMsg) { case PSCB_INITIALIZED: { WNDPROC oldWndProc; HWND refreshButtonHandle; oldWndProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)WepPropSheetWndProc); SetProp(hwndDlg, L"OldWndProc", (HANDLE)oldWndProc); // Hide the Cancel button. ShowWindow(GetDlgItem(hwndDlg, IDCANCEL), SW_HIDE); // Set the OK button's text to "Close". SetDlgItemText(hwndDlg, IDOK, L"Close"); // Add the Refresh button. refreshButtonHandle = CreateWindow(L"BUTTON", L"Refresh", WS_CHILD | WS_TABSTOP | WS_VISIBLE, 0, 0, 3, 3, hwndDlg, (HMENU)IDC_REFRESH, PluginInstance->DllBase, NULL); SendMessage(refreshButtonHandle, WM_SETFONT, (WPARAM)SendMessage(GetDlgItem(hwndDlg, IDOK), WM_GETFONT, 0, 0), FALSE); } break; } return 0; } LRESULT CALLBACK WepPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, L"OldWndProc"); switch (uMsg) { case WM_DESTROY: { SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); RemoveProp(hwnd, L"OldWndProc"); RemoveProp(hwnd, L"Moved"); } break; case WM_SHOWWINDOW: { if (!GetProp(hwnd, L"Moved")) { // Move the Refresh button to where the OK button is, and move the OK button to // where the Cancel button is. // This must be done here because in the prop sheet callback the buttons are not // in the right places. PhCopyControlRectangle(hwnd, GetDlgItem(hwnd, IDOK), GetDlgItem(hwnd, IDC_REFRESH)); PhCopyControlRectangle(hwnd, GetDlgItem(hwnd, IDCANCEL), GetDlgItem(hwnd, IDOK)); SetProp(hwnd, L"Moved", (HANDLE)1); } } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_REFRESH: { ULONG i; HWND pageHandle; // Broadcast the message to all property pages. for (i = 0; i < NUMBER_OF_PAGES; i++) { if (pageHandle = PropSheet_IndexToHwnd(hwnd, i)) SendMessage(pageHandle, WM_COMMAND, IDC_REFRESH, 0); } } break; } } break; } return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); } static HPROPSHEETPAGE WepCommonCreatePage( _In_ PWINDOW_PROPERTIES_CONTEXT Context, _In_ PWSTR Template, _In_ DLGPROC DlgProc ) { HPROPSHEETPAGE propSheetPageHandle; PROPSHEETPAGE propSheetPage; memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.dwFlags = PSP_USECALLBACK; propSheetPage.hInstance = PluginInstance->DllBase; propSheetPage.pszTemplate = Template; propSheetPage.pfnDlgProc = DlgProc; propSheetPage.lParam = (LPARAM)Context; propSheetPage.pfnCallback = WepCommonPropPageProc; propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); return propSheetPageHandle; } static INT CALLBACK WepCommonPropPageProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ LPPROPSHEETPAGE ppsp ) { PWINDOW_PROPERTIES_CONTEXT context; context = (PWINDOW_PROPERTIES_CONTEXT)ppsp->lParam; if (uMsg == PSPCB_ADDREF) WepReferenceWindowPropertiesContext(context); else if (uMsg == PSPCB_RELEASE) WepDereferenceWindowPropertiesContext(context); return 1; } NTSTATUS WepPropertiesThreadStart( _In_ PVOID Parameter ) { PH_AUTO_POOL autoPool; BOOL result; MSG message; BOOLEAN processed; ULONG i; PhInitializeAutoPool(&autoPool); WePropertiesThreadClientId = NtCurrentTeb()->ClientId; // Force the creation of the message queue so PostThreadMessage works. PeekMessage(&message, NULL, WM_USER, WM_USER, PM_NOREMOVE); PhSetEvent(&WePropertiesThreadReadyEvent); while (result = GetMessage(&message, NULL, 0, 0)) { if (result == -1) break; if (WePropertiesCreateList->Count != 0) { PhAcquireQueuedLockExclusive(&WePropertiesCreateLock); for (i = 0; i < WePropertiesCreateList->Count; i++) { PWINDOW_PROPERTIES_CONTEXT context; HWND hwnd; context = WePropertiesCreateList->Items[i]; hwnd = WepCreateWindowProperties(context); WepDereferenceWindowPropertiesContext(context); PhAddItemList(WePropertiesWindowList, hwnd); } PhClearList(WePropertiesCreateList); PhReleaseQueuedLockExclusive(&WePropertiesCreateLock); } processed = FALSE; for (i = 0; i < WePropertiesWindowList->Count; i++) { if (PropSheet_IsDialogMessage(WePropertiesWindowList->Items[i], &message)) { processed = TRUE; break; } } if (!processed) { TranslateMessage(&message); DispatchMessage(&message); } // Destroy properties windows when necessary. for (i = 0; i < WePropertiesWindowList->Count; i++) { if (!PropSheet_GetCurrentPageHwnd(WePropertiesWindowList->Items[i])) { DestroyWindow(WePropertiesWindowList->Items[i]); PhRemoveItemList(WePropertiesWindowList, i); i--; } } PhDrainAutoPool(&autoPool); } PhDeleteAutoPool(&autoPool); return STATUS_SUCCESS; } FORCEINLINE BOOLEAN WepPropPageDlgProcHeader( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ LPARAM lParam, _Out_opt_ LPPROPSHEETPAGE *PropSheetPage, _Out_opt_ PWINDOW_PROPERTIES_CONTEXT *Context ) { LPPROPSHEETPAGE propSheetPage; if (uMsg == WM_INITDIALOG) { propSheetPage = (LPPROPSHEETPAGE)lParam; // Save the context. SetProp(hwndDlg, L"PropSheetPage", (HANDLE)lParam); } else { propSheetPage = (LPPROPSHEETPAGE)GetProp(hwndDlg, L"PropSheetPage"); if (uMsg == WM_DESTROY) RemoveProp(hwndDlg, L"PropSheetPage"); } if (!propSheetPage) return FALSE; if (PropSheetPage) *PropSheetPage = propSheetPage; if (Context) *Context = (PWINDOW_PROPERTIES_CONTEXT)propSheetPage->lParam; return TRUE; } static VOID WepEnsureHookDataValid( _In_ PWINDOW_PROPERTIES_CONTEXT Context ) { if (!Context->HookDataValid) { PWE_HOOK_SHARED_DATA data; #ifdef _WIN64 HANDLE processHandle; BOOLEAN isWow64 = FALSE; #endif // The desktop window is owned by CSR. The hook will never work on the desktop window. if (Context->WindowHandle == GetDesktopWindow()) { Context->HookDataValid = TRUE; return; } #ifdef _WIN64 // We can't use the hook on WOW64 processes. if (NT_SUCCESS(PhOpenProcess(&processHandle, *(PULONG)WeGetProcedureAddress("ProcessQueryAccess"), Context->ClientId.UniqueProcess))) { PhGetProcessIsWow64(processHandle, &isWow64); NtClose(processHandle); } if (isWow64) return; #endif WeHookServerInitialization(); Context->HookDataSuccess = FALSE; if (WeLockServerSharedData(&data)) { if (WeSendServerRequest(Context->WindowHandle)) { Context->WndProc = data->c.WndProc; Context->DlgProc = data->c.DlgProc; memcpy(&Context->ClassInfo, &data->c.ClassInfo, sizeof(WNDCLASSEX)); Context->HookDataSuccess = TRUE; } WeUnlockServerSharedData(); } Context->HookDataValid = TRUE; } } static BOOLEAN NTAPI EnumGenericModulesCallback( _In_ PPH_MODULE_INFO Module, _In_opt_ PVOID Context ) { PWINDOW_PROPERTIES_CONTEXT context = Context; PhLoadModuleSymbolProvider(context->SymbolProvider, Module->FileName->Buffer, (ULONG64)Module->BaseAddress, Module->Size); return TRUE; } static NTSTATUS WepResolveSymbolFunction( _In_ PVOID Parameter ) { PSYMBOL_RESOLVE_CONTEXT context = Parameter; if (PhBeginInitOnce(&context->Context->SymbolProviderInitOnce)) { PhEnumGenericModules(context->Context->ClientId.UniqueProcess, NULL, 0, EnumGenericModulesCallback, context->Context); PhEndInitOnce(&context->Context->SymbolProviderInitOnce); } context->Symbol = PhGetSymbolFromAddress( context->Context->SymbolProvider, (ULONG64)context->Address, &context->ResolveLevel, NULL, NULL, NULL ); // Fail if we don't have a symbol. if (!context->Symbol) { WepDereferenceWindowPropertiesContext(context->Context); PhFree(context); return STATUS_SUCCESS; } PhAcquireQueuedLockExclusive(&context->Context->ResolveListLock); InsertHeadList(&context->Context->ResolveListHead, &context->ListEntry); PhReleaseQueuedLockExclusive(&context->Context->ResolveListLock); PostMessage(context->NotifyWindow, WEM_RESOLVE_DONE, 0, (LPARAM)context); WepDereferenceWindowPropertiesContext(context->Context); return STATUS_SUCCESS; } static VOID WepQueueResolveSymbol( _In_ PWINDOW_PROPERTIES_CONTEXT Context, _In_ HWND NotifyWindow, _In_ ULONG64 Address, _In_ ULONG Id ) { PSYMBOL_RESOLVE_CONTEXT resolveContext; if (!Context->SymbolProvider) { Context->SymbolProvider = PhCreateSymbolProvider(Context->ClientId.UniqueProcess); PhLoadSymbolProviderOptions(Context->SymbolProvider); } resolveContext = PhAllocate(sizeof(SYMBOL_RESOLVE_CONTEXT)); resolveContext->Address = Address; resolveContext->Symbol = NULL; resolveContext->ResolveLevel = PhsrlInvalid; resolveContext->NotifyWindow = NotifyWindow; resolveContext->Context = Context; WepReferenceWindowPropertiesContext(Context); resolveContext->Id = Id; PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), WepResolveSymbolFunction, resolveContext); } static PPH_STRING WepFormatRect( _In_ PRECT Rect ) { return PhaFormatString(L"(%d, %d) - (%d, %d) [%dx%d]", Rect->left, Rect->top, Rect->right, Rect->bottom, Rect->right - Rect->left, Rect->bottom - Rect->top); } static VOID WepRefreshWindowGeneralInfoSymbols( _In_ HWND hwndDlg, _In_ PWINDOW_PROPERTIES_CONTEXT Context ) { if (Context->WndProcResolving != 0) SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->WndProc)->Buffer); else if (Context->WndProcSymbol) SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (%s)", Context->WndProc, Context->WndProcSymbol->Buffer)->Buffer); else if (Context->WndProc != 0) SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix", Context->WndProc)->Buffer); else SetDlgItemText(hwndDlg, IDC_WINDOWPROC, L"Unknown"); if (Context->DlgProcResolving != 0) SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->DlgProc)->Buffer); else if (Context->DlgProcSymbol) SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix (%s)", Context->DlgProc, Context->DlgProcSymbol->Buffer)->Buffer); else if (Context->DlgProc != 0) SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix", Context->DlgProc)->Buffer); else if (Context->WndProc != 0) SetDlgItemText(hwndDlg, IDC_DIALOGPROC, L"N/A"); else SetDlgItemText(hwndDlg, IDC_DIALOGPROC, L"Unknown"); } static VOID WepRefreshWindowGeneralInfo( _In_ HWND hwndDlg, _In_ PWINDOW_PROPERTIES_CONTEXT Context ) { WINDOWINFO windowInfo = { sizeof(WINDOWINFO) }; WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT) }; MONITORINFO monitorInfo = { sizeof(MONITORINFO) }; SetDlgItemText(hwndDlg, IDC_THREAD, PH_AUTO_T(PH_STRING, PhGetClientIdName(&Context->ClientId))->Buffer); SetDlgItemText(hwndDlg, IDC_TEXT, PhGetStringOrEmpty(PH_AUTO(PhGetWindowText(Context->WindowHandle)))); if (GetWindowInfo(Context->WindowHandle, &windowInfo)) { SetDlgItemText(hwndDlg, IDC_RECTANGLE, WepFormatRect(&windowInfo.rcWindow)->Buffer); SetDlgItemText(hwndDlg, IDC_CLIENTRECTANGLE, WepFormatRect(&windowInfo.rcClient)->Buffer); } else { SetDlgItemText(hwndDlg, IDC_RECTANGLE, L"N/A"); SetDlgItemText(hwndDlg, IDC_CLIENTRECTANGLE, L"N/A"); } if (GetWindowPlacement(Context->WindowHandle, &windowPlacement)) { // The rectangle is in workspace coordinates. Convert the values back to screen coordinates. if (GetMonitorInfo(MonitorFromRect(&windowPlacement.rcNormalPosition, MONITOR_DEFAULTTOPRIMARY), &monitorInfo)) { windowPlacement.rcNormalPosition.left += monitorInfo.rcWork.left; windowPlacement.rcNormalPosition.top += monitorInfo.rcWork.top; windowPlacement.rcNormalPosition.right += monitorInfo.rcWork.left; windowPlacement.rcNormalPosition.bottom += monitorInfo.rcWork.top; } SetDlgItemText(hwndDlg, IDC_NORMALRECTANGLE, WepFormatRect(&windowPlacement.rcNormalPosition)->Buffer); } else { SetDlgItemText(hwndDlg, IDC_NORMALRECTANGLE, L"N/A"); } SetDlgItemText(hwndDlg, IDC_INSTANCEHANDLE, PhaFormatString(L"0x%Ix", GetWindowLongPtr(Context->WindowHandle, GWLP_HINSTANCE))->Buffer); SetDlgItemText(hwndDlg, IDC_MENUHANDLE, PhaFormatString(L"0x%Ix", GetMenu(Context->WindowHandle))->Buffer); SetDlgItemText(hwndDlg, IDC_USERDATA, PhaFormatString(L"0x%Ix", GetWindowLongPtr(Context->WindowHandle, GWLP_USERDATA))->Buffer); SetDlgItemText(hwndDlg, IDC_UNICODE, IsWindowUnicode(Context->WindowHandle) ? L"Yes" : L"No"); SetDlgItemText(hwndDlg, IDC_CTRLID, PhaFormatString(L"%lu", GetDlgCtrlID(Context->WindowHandle))->Buffer); WepEnsureHookDataValid(Context); if (Context->WndProc != 0) { Context->WndProcResolving++; WepQueueResolveSymbol(Context, hwndDlg, Context->WndProc, 1); } if (Context->DlgProc != 0) { Context->DlgProcResolving++; WepQueueResolveSymbol(Context, hwndDlg, Context->DlgProc, 2); } WepRefreshWindowGeneralInfoSymbols(hwndDlg, Context); } INT_PTR CALLBACK WepWindowGeneralDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { PWINDOW_PROPERTIES_CONTEXT context; if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context)) return FALSE; switch (uMsg) { case WM_INITDIALOG: { WepRefreshWindowGeneralInfo(hwndDlg, context); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_REFRESH: context->HookDataValid = FALSE; PhClearReference(&context->WndProcSymbol); WepRefreshWindowGeneralInfo(hwndDlg, context); break; } } break; case WEM_RESOLVE_DONE: { PSYMBOL_RESOLVE_CONTEXT resolveContext = (PSYMBOL_RESOLVE_CONTEXT)lParam; if (resolveContext->Id == 1) { PhAcquireQueuedLockExclusive(&context->ResolveListLock); RemoveEntryList(&resolveContext->ListEntry); PhReleaseQueuedLockExclusive(&context->ResolveListLock); if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) PhClearReference(&resolveContext->Symbol); PhMoveReference(&context->WndProcSymbol, resolveContext->Symbol); PhFree(resolveContext); context->WndProcResolving--; } else if (resolveContext->Id == 2) { PhAcquireQueuedLockExclusive(&context->ResolveListLock); RemoveEntryList(&resolveContext->ListEntry); PhReleaseQueuedLockExclusive(&context->ResolveListLock); if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) PhClearReference(&resolveContext->Symbol); PhMoveReference(&context->DlgProcSymbol, resolveContext->Symbol); PhFree(resolveContext); context->DlgProcResolving--; } WepRefreshWindowGeneralInfoSymbols(hwndDlg, context); } break; } return FALSE; } static VOID WepRefreshWindowStyles( _In_ HWND hwndDlg, _In_ PWINDOW_PROPERTIES_CONTEXT Context ) { WINDOWINFO windowInfo = { sizeof(WINDOWINFO) }; HWND stylesListBox; HWND extendedStylesListBox; ULONG i; stylesListBox = GetDlgItem(hwndDlg, IDC_STYLESLIST); extendedStylesListBox = GetDlgItem(hwndDlg, IDC_EXTENDEDSTYLESLIST); ListBox_ResetContent(stylesListBox); ListBox_ResetContent(extendedStylesListBox); if (GetWindowInfo(Context->WindowHandle, &windowInfo)) { SetDlgItemText(hwndDlg, IDC_STYLES, PhaFormatString(L"0x%x", windowInfo.dwStyle)->Buffer); SetDlgItemText(hwndDlg, IDC_EXTENDEDSTYLES, PhaFormatString(L"0x%x", windowInfo.dwExStyle)->Buffer); for (i = 0; i < sizeof(WepStylePairs) / sizeof(STRING_INTEGER_PAIR); i++) { if (windowInfo.dwStyle & WepStylePairs[i].Integer) { // Skip irrelevant styles. if (WepStylePairs[i].Integer == WS_MAXIMIZEBOX || WepStylePairs[i].Integer == WS_MINIMIZEBOX) { if (windowInfo.dwStyle & WS_CHILD) continue; } if (WepStylePairs[i].Integer == WS_TABSTOP || WepStylePairs[i].Integer == WS_GROUP) { if (!(windowInfo.dwStyle & WS_CHILD)) continue; } ListBox_AddString(stylesListBox, WepStylePairs[i].String); } } for (i = 0; i < sizeof(WepExtendedStylePairs) / sizeof(STRING_INTEGER_PAIR); i++) { if (windowInfo.dwExStyle & WepExtendedStylePairs[i].Integer) { ListBox_AddString(extendedStylesListBox, WepExtendedStylePairs[i].String); } } } else { SetDlgItemText(hwndDlg, IDC_STYLES, L"N/A"); SetDlgItemText(hwndDlg, IDC_EXTENDEDSTYLES, L"N/A"); } } INT_PTR CALLBACK WepWindowStylesDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { PWINDOW_PROPERTIES_CONTEXT context; if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context)) return FALSE; switch (uMsg) { case WM_INITDIALOG: { WepRefreshWindowStyles(hwndDlg, context); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_REFRESH: WepRefreshWindowStyles(hwndDlg, context); break; } } break; } return FALSE; } static VOID WepRefreshWindowClassInfoSymbols( _In_ HWND hwndDlg, _In_ PWINDOW_PROPERTIES_CONTEXT Context ) { if (Context->ClassWndProcResolving != 0) SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->ClassInfo.lpfnWndProc)->Buffer); else if (Context->ClassWndProcSymbol) SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (%s)", Context->ClassInfo.lpfnWndProc, Context->ClassWndProcSymbol->Buffer)->Buffer); else if (Context->ClassInfo.lpfnWndProc) SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix", Context->ClassInfo.lpfnWndProc)->Buffer); else SetDlgItemText(hwndDlg, IDC_WINDOWPROC, L"Unknown"); } static VOID WepRefreshWindowClassInfo( _In_ HWND hwndDlg, _In_ PWINDOW_PROPERTIES_CONTEXT Context ) { WCHAR className[256]; PH_STRING_BUILDER stringBuilder; ULONG i; if (!GetClassName(Context->WindowHandle, className, sizeof(className) / sizeof(WCHAR))) className[0] = 0; WepEnsureHookDataValid(Context); if (!Context->HookDataSuccess) { Context->ClassInfo.cbSize = sizeof(WNDCLASSEX); GetClassInfoEx(NULL, className, &Context->ClassInfo); } SetDlgItemText(hwndDlg, IDC_NAME, className); SetDlgItemText(hwndDlg, IDC_ATOM, PhaFormatString(L"0x%x", GetClassWord(Context->WindowHandle, GCW_ATOM))->Buffer); SetDlgItemText(hwndDlg, IDC_INSTANCEHANDLE, PhaFormatString(L"0x%Ix", GetClassLongPtr(Context->WindowHandle, GCLP_HMODULE))->Buffer); SetDlgItemText(hwndDlg, IDC_ICONHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hIcon)->Buffer); SetDlgItemText(hwndDlg, IDC_SMALLICONHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hIconSm)->Buffer); SetDlgItemText(hwndDlg, IDC_MENUNAME, PhaFormatString(L"0x%Ix", Context->ClassInfo.lpszMenuName)->Buffer); PhInitializeStringBuilder(&stringBuilder, 100); PhAppendFormatStringBuilder(&stringBuilder, L"0x%x (", Context->ClassInfo.style); for (i = 0; i < sizeof(WepClassStylePairs) / sizeof(STRING_INTEGER_PAIR); i++) { if (Context->ClassInfo.style & WepClassStylePairs[i].Integer) { PhAppendStringBuilder2(&stringBuilder, WepClassStylePairs[i].String); PhAppendStringBuilder2(&stringBuilder, L" | "); } } if (PhEndsWithString2(stringBuilder.String, L" | ", FALSE)) { PhRemoveEndStringBuilder(&stringBuilder, 3); PhAppendCharStringBuilder(&stringBuilder, ')'); } else { // No styles. Remove the brackets. PhRemoveEndStringBuilder(&stringBuilder, 1); } SetDlgItemText(hwndDlg, IDC_STYLES, stringBuilder.String->Buffer); PhDeleteStringBuilder(&stringBuilder); // TODO: Add symbols for these values. SetDlgItemText(hwndDlg, IDC_CURSORHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hCursor)->Buffer); SetDlgItemText(hwndDlg, IDC_BACKGROUNDBRUSH, PhaFormatString(L"0x%Ix", Context->ClassInfo.hbrBackground)->Buffer); if (Context->ClassInfo.lpfnWndProc) { Context->ClassWndProcResolving++; WepQueueResolveSymbol(Context, hwndDlg, (ULONG_PTR)Context->ClassInfo.lpfnWndProc, 0); } WepRefreshWindowClassInfoSymbols(hwndDlg, Context); } INT_PTR CALLBACK WepWindowClassDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { PWINDOW_PROPERTIES_CONTEXT context; if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context)) return FALSE; switch (uMsg) { case WM_INITDIALOG: { WepRefreshWindowClassInfo(hwndDlg, context); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_REFRESH: context->HookDataValid = FALSE; PhClearReference(&context->ClassWndProcSymbol); WepRefreshWindowClassInfo(hwndDlg, context); break; } } break; case WEM_RESOLVE_DONE: { PSYMBOL_RESOLVE_CONTEXT resolveContext = (PSYMBOL_RESOLVE_CONTEXT)lParam; PhAcquireQueuedLockExclusive(&context->ResolveListLock); RemoveEntryList(&resolveContext->ListEntry); PhReleaseQueuedLockExclusive(&context->ResolveListLock); if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) PhClearReference(&resolveContext->Symbol); PhMoveReference(&context->ClassWndProcSymbol, resolveContext->Symbol); PhFree(resolveContext); context->ClassWndProcResolving--; WepRefreshWindowClassInfoSymbols(hwndDlg, context); } break; } return FALSE; } static BOOL CALLBACK EnumPropsExCallback( _In_ HWND hwnd, _In_ LPTSTR lpszString, _In_ HANDLE hData, _In_ ULONG_PTR dwData ) { INT lvItemIndex; PWSTR propName; WCHAR value[PH_PTR_STR_LEN_1]; propName = lpszString; if ((ULONG_PTR)lpszString < USHRT_MAX) { // This is an integer atom. propName = PhaFormatString(L"#%lu", (ULONG_PTR)lpszString)->Buffer; } lvItemIndex = PhAddListViewItem((HWND)dwData, MAXINT, propName, NULL); PhPrintPointer(value, (PVOID)hData); PhSetListViewSubItem((HWND)dwData, lvItemIndex, 1, value); return TRUE; } static VOID WepRefreshWindowProps( _In_ HWND hwndDlg, _In_ HWND ListViewHandle, _In_ PWINDOW_PROPERTIES_CONTEXT Context ) { ExtendedListView_SetRedraw(ListViewHandle, FALSE); ListView_DeleteAllItems(ListViewHandle); EnumPropsEx(Context->WindowHandle, EnumPropsExCallback, (LPARAM)ListViewHandle); ExtendedListView_SortItems(ListViewHandle); ExtendedListView_SetRedraw(ListViewHandle, TRUE); } INT_PTR CALLBACK WepWindowPropertiesDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { PWINDOW_PROPERTIES_CONTEXT context; if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context)) return FALSE; switch (uMsg) { case WM_INITDIALOG: { HWND lvHandle; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); PhSetListViewStyle(lvHandle, FALSE, TRUE); PhSetControlTheme(lvHandle, L"explorer"); PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Value"); PhSetExtendedListView(lvHandle); WepRefreshWindowProps(hwndDlg, lvHandle, context); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_REFRESH: WepRefreshWindowProps(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), context); break; } } break; } return FALSE; }