2025-05-13 19:45:22 +03:00

1208 lines
36 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*/
// 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 <workqueue.h>
#include <symprv.h>
#include <windowsx.h>
#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;
}