ProcessHacker/phlib/guisup.c
2025-05-13 19:45:22 +03:00

1373 lines
33 KiB
C

/*
* Process Hacker -
* GUI support functions
*
* Copyright (C) 2009-2016 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/>.
*/
#include <ph.h>
#include <guisup.h>
#include <guisupp.h>
#include <shellapi.h>
#include <windowsx.h>
#include <uxtheme.h>
#define SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96)
_ChangeWindowMessageFilter ChangeWindowMessageFilter_I;
_IsImmersiveProcess IsImmersiveProcess_I;
_RunFileDlg RunFileDlg;
_SHAutoComplete SHAutoComplete_I;
static PH_INITONCE SharedIconCacheInitOnce = PH_INITONCE_INIT;
static PPH_HASHTABLE SharedIconCacheHashtable;
static PH_QUEUED_LOCK SharedIconCacheLock = PH_QUEUED_LOCK_INIT;
VOID PhGuiSupportInitialization(
VOID
)
{
HMODULE shell32Handle;
HMODULE shlwapiHandle;
shell32Handle = LoadLibrary(L"shell32.dll");
shlwapiHandle = LoadLibrary(L"shlwapi.dll");
if (WINDOWS_HAS_UAC)
ChangeWindowMessageFilter_I = PhGetModuleProcAddress(L"user32.dll", "ChangeWindowMessageFilter");
if (WINDOWS_HAS_IMMERSIVE)
IsImmersiveProcess_I = PhGetModuleProcAddress(L"user32.dll", "IsImmersiveProcess");
RunFileDlg = PhGetProcedureAddress(shell32Handle, NULL, 61);
SHAutoComplete_I = PhGetProcedureAddress(shlwapiHandle, "SHAutoComplete", 0);
}
VOID PhSetControlTheme(
_In_ HWND Handle,
_In_ PWSTR Theme
)
{
if (WindowsVersion >= WINDOWS_VISTA)
{
SetWindowTheme(Handle, Theme, NULL);
}
}
INT PhAddListViewColumn(
_In_ HWND ListViewHandle,
_In_ INT Index,
_In_ INT DisplayIndex,
_In_ INT SubItemIndex,
_In_ INT Format,
_In_ INT Width,
_In_ PWSTR Text
)
{
LVCOLUMN column;
column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
column.fmt = Format;
column.cx = Width < 0 ? -Width : SCALE_DPI(Width);
column.pszText = Text;
column.iSubItem = SubItemIndex;
column.iOrder = DisplayIndex;
return ListView_InsertColumn(ListViewHandle, Index, &column);
}
INT PhAddListViewItem(
_In_ HWND ListViewHandle,
_In_ INT Index,
_In_ PWSTR Text,
_In_opt_ PVOID Param
)
{
LVITEM item;
item.mask = LVIF_TEXT | LVIF_PARAM;
item.iItem = Index;
item.iSubItem = 0;
item.pszText = Text;
item.lParam = (LPARAM)Param;
return ListView_InsertItem(ListViewHandle, &item);
}
INT PhFindListViewItemByFlags(
_In_ HWND ListViewHandle,
_In_ INT StartIndex,
_In_ ULONG Flags
)
{
return ListView_GetNextItem(ListViewHandle, StartIndex, Flags);
}
INT PhFindListViewItemByParam(
_In_ HWND ListViewHandle,
_In_ INT StartIndex,
_In_opt_ PVOID Param
)
{
LVFINDINFO findInfo;
findInfo.flags = LVFI_PARAM;
findInfo.lParam = (LPARAM)Param;
return ListView_FindItem(ListViewHandle, StartIndex, &findInfo);
}
LOGICAL PhGetListViewItemImageIndex(
_In_ HWND ListViewHandle,
_In_ INT Index,
_Out_ PINT ImageIndex
)
{
LOGICAL result;
LVITEM item;
item.mask = LVIF_IMAGE;
item.iItem = Index;
item.iSubItem = 0;
result = ListView_GetItem(ListViewHandle, &item);
if (!result)
return result;
*ImageIndex = item.iImage;
return result;
}
LOGICAL PhGetListViewItemParam(
_In_ HWND ListViewHandle,
_In_ INT Index,
_Out_ PVOID *Param
)
{
LOGICAL result;
LVITEM item;
item.mask = LVIF_PARAM;
item.iItem = Index;
item.iSubItem = 0;
result = ListView_GetItem(ListViewHandle, &item);
if (!result)
return result;
*Param = (PVOID)item.lParam;
return result;
}
VOID PhRemoveListViewItem(
_In_ HWND ListViewHandle,
_In_ INT Index
)
{
ListView_DeleteItem(ListViewHandle, Index);
}
VOID PhSetListViewItemImageIndex(
_In_ HWND ListViewHandle,
_In_ INT Index,
_In_ INT ImageIndex
)
{
LVITEM item;
item.mask = LVIF_IMAGE;
item.iItem = Index;
item.iSubItem = 0;
item.iImage = ImageIndex;
ListView_SetItem(ListViewHandle, &item);
}
VOID PhSetListViewSubItem(
_In_ HWND ListViewHandle,
_In_ INT Index,
_In_ INT SubItemIndex,
_In_ PWSTR Text
)
{
LVITEM item;
item.mask = LVIF_TEXT;
item.iItem = Index;
item.iSubItem = SubItemIndex;
item.pszText = Text;
ListView_SetItem(ListViewHandle, &item);
}
BOOLEAN PhLoadListViewColumnSettings(
_In_ HWND ListViewHandle,
_In_ PPH_STRING Settings
)
{
#define ORDER_LIMIT 50
PH_STRINGREF remainingPart;
ULONG columnIndex;
ULONG orderArray[ORDER_LIMIT]; // HACK, but reasonable limit
ULONG maxOrder;
ULONG scale;
if (Settings->Length == 0)
return FALSE;
remainingPart = Settings->sr;
columnIndex = 0;
memset(orderArray, 0, sizeof(orderArray));
maxOrder = 0;
if (remainingPart.Length != 0 && remainingPart.Buffer[0] == '@')
{
PH_STRINGREF scalePart;
ULONG64 integer;
PhSkipStringRef(&remainingPart, sizeof(WCHAR));
PhSplitStringRefAtChar(&remainingPart, '|', &scalePart, &remainingPart);
if (scalePart.Length == 0 || !PhStringToInteger64(&scalePart, 10, &integer))
return FALSE;
scale = (ULONG)integer;
}
else
{
scale = PhGlobalDpi;
}
while (remainingPart.Length != 0)
{
PH_STRINGREF columnPart;
PH_STRINGREF orderPart;
PH_STRINGREF widthPart;
ULONG64 integer;
ULONG order;
ULONG width;
LVCOLUMN lvColumn;
PhSplitStringRefAtChar(&remainingPart, '|', &columnPart, &remainingPart);
if (columnPart.Length == 0)
return FALSE;
PhSplitStringRefAtChar(&columnPart, ',', &orderPart, &widthPart);
if (orderPart.Length == 0 || widthPart.Length == 0)
return FALSE;
// Order
if (!PhStringToInteger64(&orderPart, 10, &integer))
return FALSE;
order = (ULONG)integer;
if (order < ORDER_LIMIT)
{
orderArray[order] = columnIndex;
if (maxOrder < order + 1)
maxOrder = order + 1;
}
// Width
if (!PhStringToInteger64(&widthPart, 10, &integer))
return FALSE;
width = (ULONG)integer;
if (scale != PhGlobalDpi && scale != 0)
width = PhMultiplyDivide(width, PhGlobalDpi, scale);
lvColumn.mask = LVCF_WIDTH;
lvColumn.cx = width;
ListView_SetColumn(ListViewHandle, columnIndex, &lvColumn);
columnIndex++;
}
ListView_SetColumnOrderArray(ListViewHandle, maxOrder, orderArray);
return TRUE;
}
PPH_STRING PhSaveListViewColumnSettings(
_In_ HWND ListViewHandle
)
{
PH_STRING_BUILDER stringBuilder;
ULONG i = 0;
LVCOLUMN lvColumn;
PhInitializeStringBuilder(&stringBuilder, 20);
PhAppendFormatStringBuilder(&stringBuilder, L"@%u|", PhGlobalDpi);
lvColumn.mask = LVCF_WIDTH | LVCF_ORDER;
while (ListView_GetColumn(ListViewHandle, i, &lvColumn))
{
PhAppendFormatStringBuilder(
&stringBuilder,
L"%u,%u|",
lvColumn.iOrder,
lvColumn.cx
);
i++;
}
if (stringBuilder.String->Length != 0)
PhRemoveEndStringBuilder(&stringBuilder, 1);
return PhFinalStringBuilderString(&stringBuilder);
}
INT PhAddTabControlTab(
_In_ HWND TabControlHandle,
_In_ INT Index,
_In_ PWSTR Text
)
{
TCITEM item;
item.mask = TCIF_TEXT;
item.pszText = Text;
return TabCtrl_InsertItem(TabControlHandle, Index, &item);
}
PPH_STRING PhGetWindowText(
_In_ HWND hwnd
)
{
PPH_STRING text;
PhGetWindowTextEx(hwnd, 0, &text);
return text;
}
ULONG PhGetWindowTextEx(
_In_ HWND hwnd,
_In_ ULONG Flags,
_Out_opt_ PPH_STRING *Text
)
{
PPH_STRING string;
ULONG length;
if (Flags & PH_GET_WINDOW_TEXT_INTERNAL)
{
if (Flags & PH_GET_WINDOW_TEXT_LENGTH_ONLY)
{
WCHAR buffer[32];
length = InternalGetWindowText(hwnd, buffer, sizeof(buffer) / sizeof(WCHAR));
}
else
{
// TODO: Resize the buffer until we get the entire thing.
string = PhCreateStringEx(NULL, 256 * sizeof(WCHAR));
length = InternalGetWindowText(hwnd, string->Buffer, (ULONG)string->Length / sizeof(WCHAR) + 1);
string->Length = length * sizeof(WCHAR);
if (Text)
*Text = string;
else
PhDereferenceObject(string);
}
return length;
}
else
{
length = GetWindowTextLength(hwnd);
if (length == 0 || (Flags & PH_GET_WINDOW_TEXT_LENGTH_ONLY))
{
if (Text)
*Text = PhReferenceEmptyString();
return length;
}
string = PhCreateStringEx(NULL, length * sizeof(WCHAR));
if (GetWindowText(hwnd, string->Buffer, (ULONG)string->Length / sizeof(WCHAR) + 1))
{
if (Text)
*Text = string;
else
PhDereferenceObject(string);
return length;
}
else
{
if (Text)
*Text = PhReferenceEmptyString();
PhDereferenceObject(string);
return 0;
}
}
}
VOID PhAddComboBoxStrings(
_In_ HWND hWnd,
_In_ PWSTR *Strings,
_In_ ULONG NumberOfStrings
)
{
ULONG i;
for (i = 0; i < NumberOfStrings; i++)
ComboBox_AddString(hWnd, Strings[i]);
}
PPH_STRING PhGetComboBoxString(
_In_ HWND hwnd,
_In_ INT Index
)
{
PPH_STRING string;
ULONG length;
if (Index == -1)
{
Index = ComboBox_GetCurSel(hwnd);
if (Index == -1)
return NULL;
}
length = ComboBox_GetLBTextLen(hwnd, Index);
if (length == CB_ERR)
return NULL;
if (length == 0)
return PhReferenceEmptyString();
string = PhCreateStringEx(NULL, length * 2);
if (ComboBox_GetLBText(hwnd, Index, string->Buffer) != CB_ERR)
{
return string;
}
else
{
PhDereferenceObject(string);
return NULL;
}
}
INT PhSelectComboBoxString(
_In_ HWND hwnd,
_In_ PWSTR String,
_In_ BOOLEAN Partial
)
{
if (Partial)
{
return ComboBox_SelectString(hwnd, -1, String);
}
else
{
INT index;
index = ComboBox_FindStringExact(hwnd, -1, String);
if (index == CB_ERR)
return CB_ERR;
ComboBox_SetCurSel(hwnd, index);
return index;
}
}
PPH_STRING PhGetListBoxString(
_In_ HWND hwnd,
_In_ INT Index
)
{
PPH_STRING string;
ULONG length;
if (Index == -1)
{
Index = ListBox_GetCurSel(hwnd);
if (Index == -1)
return NULL;
}
length = ListBox_GetTextLen(hwnd, Index);
if (length == LB_ERR)
return NULL;
if (length == 0)
return PhReferenceEmptyString();
string = PhCreateStringEx(NULL, length * 2);
if (ListBox_GetText(hwnd, Index, string->Buffer) != LB_ERR)
{
return string;
}
else
{
PhDereferenceObject(string);
return NULL;
}
}
VOID PhSetStateAllListViewItems(
_In_ HWND hWnd,
_In_ ULONG State,
_In_ ULONG Mask
)
{
ULONG i;
ULONG count;
count = ListView_GetItemCount(hWnd);
if (count == -1)
return;
for (i = 0; i < count; i++)
{
ListView_SetItemState(hWnd, i, State, Mask);
}
}
PVOID PhGetSelectedListViewItemParam(
_In_ HWND hWnd
)
{
INT index;
PVOID param;
index = PhFindListViewItemByFlags(
hWnd,
-1,
LVNI_SELECTED
);
if (index != -1)
{
if (PhGetListViewItemParam(
hWnd,
index,
&param
))
{
return param;
}
}
return NULL;
}
VOID PhGetSelectedListViewItemParams(
_In_ HWND hWnd,
_Out_ PVOID **Items,
_Out_ PULONG NumberOfItems
)
{
PH_ARRAY array;
ULONG index;
PVOID param;
PhInitializeArray(&array, sizeof(PVOID), 2);
index = -1;
while ((index = PhFindListViewItemByFlags(
hWnd,
index,
LVNI_SELECTED
)) != -1)
{
if (PhGetListViewItemParam(hWnd, index, &param))
PhAddItemArray(&array, &param);
}
*NumberOfItems = (ULONG)array.Count;
*Items = PhFinalArrayItems(&array);
}
VOID PhSetImageListBitmap(
_In_ HIMAGELIST ImageList,
_In_ INT Index,
_In_ HINSTANCE InstanceHandle,
_In_ LPCWSTR BitmapName
)
{
HBITMAP bitmap;
bitmap = LoadImage(InstanceHandle, BitmapName, IMAGE_BITMAP, 0, 0, 0);
if (bitmap)
{
ImageList_Replace(ImageList, Index, bitmap, NULL);
DeleteObject(bitmap);
}
}
static BOOLEAN SharedIconCacheHashtableEqualFunction(
_In_ PVOID Entry1,
_In_ PVOID Entry2
)
{
PPHP_ICON_ENTRY entry1 = Entry1;
PPHP_ICON_ENTRY entry2 = Entry2;
if (entry1->InstanceHandle != entry2->InstanceHandle ||
entry1->Width != entry2->Width ||
entry1->Height != entry2->Height)
{
return FALSE;
}
if (IS_INTRESOURCE(entry1->Name))
{
if (IS_INTRESOURCE(entry2->Name))
return entry1->Name == entry2->Name;
else
return FALSE;
}
else
{
if (!IS_INTRESOURCE(entry2->Name))
return PhEqualStringZ(entry1->Name, entry2->Name, FALSE);
else
return FALSE;
}
}
static ULONG SharedIconCacheHashtableHashFunction(
_In_ PVOID Entry
)
{
PPHP_ICON_ENTRY entry = Entry;
ULONG nameHash;
if (IS_INTRESOURCE(entry->Name))
nameHash = PtrToUlong(entry->Name);
else
nameHash = PhHashBytes((PUCHAR)entry->Name, PhCountStringZ(entry->Name));
return nameHash ^ (PtrToUlong(entry->InstanceHandle) >> 5) ^ (entry->Width << 3) ^ entry->Height;
}
HICON PhLoadIcon(
_In_opt_ HINSTANCE InstanceHandle,
_In_ PWSTR Name,
_In_ ULONG Flags,
_In_opt_ ULONG Width,
_In_opt_ ULONG Height
)
{
static _LoadIconMetric loadIconMetric;
static _LoadIconWithScaleDown loadIconWithScaleDown;
PHP_ICON_ENTRY entry;
PPHP_ICON_ENTRY actualEntry;
HICON icon = NULL;
if (PhBeginInitOnce(&SharedIconCacheInitOnce))
{
loadIconMetric = (_LoadIconMetric)PhGetModuleProcAddress(L"comctl32.dll", "LoadIconMetric");
loadIconWithScaleDown = (_LoadIconWithScaleDown)PhGetModuleProcAddress(L"comctl32.dll", "LoadIconWithScaleDown");
SharedIconCacheHashtable = PhCreateHashtable(sizeof(PHP_ICON_ENTRY),
SharedIconCacheHashtableEqualFunction, SharedIconCacheHashtableHashFunction, 10);
PhEndInitOnce(&SharedIconCacheInitOnce);
}
if (Flags & PH_LOAD_ICON_SHARED)
{
PhAcquireQueuedLockExclusive(&SharedIconCacheLock);
entry.InstanceHandle = InstanceHandle;
entry.Name = Name;
entry.Width = PhpGetIconEntrySize(Width, Flags);
entry.Height = PhpGetIconEntrySize(Height, Flags);
actualEntry = PhFindEntryHashtable(SharedIconCacheHashtable, &entry);
if (actualEntry)
{
icon = actualEntry->Icon;
PhReleaseQueuedLockExclusive(&SharedIconCacheLock);
return icon;
}
}
if (Flags & (PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_SIZE_LARGE))
{
if (loadIconMetric)
loadIconMetric(InstanceHandle, Name, (Flags & PH_LOAD_ICON_SIZE_SMALL) ? LIM_SMALL : LIM_LARGE, &icon);
}
else
{
if (loadIconWithScaleDown)
loadIconWithScaleDown(InstanceHandle, Name, Width, Height, &icon);
}
if (!icon && !(Flags & PH_LOAD_ICON_STRICT))
{
if (Flags & PH_LOAD_ICON_SIZE_SMALL)
{
static ULONG smallWidth = 0;
static ULONG smallHeight = 0;
if (!smallWidth)
smallWidth = GetSystemMetrics(SM_CXSMICON);
if (!smallHeight)
smallHeight = GetSystemMetrics(SM_CYSMICON);
Width = smallWidth;
Height = smallHeight;
}
else if (Flags & PH_LOAD_ICON_SIZE_LARGE)
{
static ULONG largeWidth = 0;
static ULONG largeHeight = 0;
if (!largeWidth)
largeWidth = GetSystemMetrics(SM_CXICON);
if (!largeHeight)
largeHeight = GetSystemMetrics(SM_CYICON);
Width = largeWidth;
Height = largeHeight;
}
icon = LoadImage(InstanceHandle, Name, IMAGE_ICON, Width, Height, 0);
}
if (Flags & PH_LOAD_ICON_SHARED)
{
if (icon)
{
if (!IS_INTRESOURCE(Name))
entry.Name = PhDuplicateStringZ(Name);
entry.Icon = icon;
PhAddEntryHashtable(SharedIconCacheHashtable, &entry);
}
PhReleaseQueuedLockExclusive(&SharedIconCacheLock);
}
return icon;
}
/**
* Gets the default icon used for executable files.
*
* \param SmallIcon A variable which receives the small default executable icon. Do not destroy the
* icon using DestroyIcon(); it is shared between callers.
* \param LargeIcon A variable which receives the large default executable icon. Do not destroy the
* icon using DestroyIcon(); it is shared between callers.
*/
VOID PhGetStockApplicationIcon(
_Out_opt_ HICON *SmallIcon,
_Out_opt_ HICON *LargeIcon
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static HICON smallIcon = NULL;
static HICON largeIcon = NULL;
// This no longer uses SHGetFileInfo because it is *very* slow and causes many other DLLs to be
// loaded, increasing memory usage. The worst thing about it, however, is that it is horribly
// incompatible with multi-threading. The first time it is called, it tries to perform some
// one-time initialization. It guards this with a lock, but when multiple threads try to call
// the function at the same time, instead of waiting for initialization to finish it simply
// fails the other threads.
if (PhBeginInitOnce(&initOnce))
{
PPH_STRING systemDirectory;
PPH_STRING dllFileName;
// imageres,11 (Windows 10 and above), user32,0 (Vista and above) or shell32,2 (XP) contains
// the default application icon.
if (systemDirectory = PhGetSystemDirectory())
{
PH_STRINGREF dllBaseName;
ULONG index;
// TODO: Find a better solution.
if (WindowsVersion >= WINDOWS_10)
{
PhInitializeStringRef(&dllBaseName, L"\\imageres.dll");
index = 11;
}
else if (WindowsVersion >= WINDOWS_VISTA)
{
PhInitializeStringRef(&dllBaseName, L"\\user32.dll");
index = 0;
}
else
{
PhInitializeStringRef(&dllBaseName, L"\\shell32.dll");
index = 2;
}
dllFileName = PhConcatStringRef2(&systemDirectory->sr, &dllBaseName);
PhDereferenceObject(systemDirectory);
ExtractIconEx(dllFileName->Buffer, index, &largeIcon, &smallIcon, 1);
PhDereferenceObject(dllFileName);
}
// Fallback icons - this is bad, because the icon isn't scaled correctly.
if (!smallIcon)
smallIcon = LoadIcon(NULL, IDI_APPLICATION);
if (!largeIcon)
largeIcon = LoadIcon(NULL, IDI_APPLICATION);
PhEndInitOnce(&initOnce);
}
if (SmallIcon)
*SmallIcon = smallIcon;
if (LargeIcon)
*LargeIcon = largeIcon;
}
HICON PhGetFileShellIcon(
_In_opt_ PWSTR FileName,
_In_opt_ PWSTR DefaultExtension,
_In_ BOOLEAN LargeIcon
)
{
SHFILEINFO fileInfo;
ULONG iconFlag;
HICON icon;
if (DefaultExtension && PhEqualStringZ(DefaultExtension, L".exe", TRUE))
{
// Special case for executable files (see above for reasoning).
icon = NULL;
if (FileName)
{
ExtractIconEx(
FileName,
0,
LargeIcon ? &icon : NULL,
!LargeIcon ? &icon : NULL,
1
);
}
if (!icon)
{
PhGetStockApplicationIcon(
!LargeIcon ? &icon : NULL,
LargeIcon ? &icon : NULL
);
if (icon)
icon = DuplicateIcon(NULL, icon);
}
return icon;
}
iconFlag = LargeIcon ? SHGFI_LARGEICON : SHGFI_SMALLICON;
icon = NULL;
if (FileName && SHGetFileInfo(
FileName,
0,
&fileInfo,
sizeof(SHFILEINFO),
SHGFI_ICON | iconFlag
))
{
icon = fileInfo.hIcon;
}
if (!icon && DefaultExtension)
{
if (SHGetFileInfo(
DefaultExtension,
FILE_ATTRIBUTE_NORMAL,
&fileInfo,
sizeof(SHFILEINFO),
SHGFI_ICON | iconFlag | SHGFI_USEFILEATTRIBUTES
))
icon = fileInfo.hIcon;
}
return icon;
}
VOID PhpSetClipboardData(
_In_ HWND hWnd,
_In_ ULONG Format,
_In_ HANDLE Data
)
{
if (OpenClipboard(hWnd))
{
if (!EmptyClipboard())
goto Fail;
if (!SetClipboardData(Format, Data))
goto Fail;
CloseClipboard();
return;
}
Fail:
GlobalFree(Data);
}
VOID PhSetClipboardString(
_In_ HWND hWnd,
_In_ PPH_STRINGREF String
)
{
HANDLE data;
PVOID memory;
data = GlobalAlloc(GMEM_MOVEABLE, String->Length + sizeof(WCHAR));
memory = GlobalLock(data);
memcpy(memory, String->Buffer, String->Length);
*(PWCHAR)((PCHAR)memory + String->Length) = 0;
GlobalUnlock(memory);
PhpSetClipboardData(hWnd, CF_UNICODETEXT, data);
}
HWND PhCreateDialogFromTemplate(
_In_ HWND Parent,
_In_ ULONG Style,
_In_ PVOID Instance,
_In_ PWSTR Template,
_In_ DLGPROC DialogProc,
_In_ PVOID Parameter
)
{
HRSRC resourceInfo;
ULONG resourceSize;
HGLOBAL resourceHandle;
PDLGTEMPLATEEX dialog;
PDLGTEMPLATEEX dialogCopy;
HWND dialogHandle;
resourceInfo = FindResource(Instance, Template, MAKEINTRESOURCE(RT_DIALOG));
if (!resourceInfo)
return NULL;
resourceSize = SizeofResource(Instance, resourceInfo);
if (resourceSize == 0)
return NULL;
resourceHandle = LoadResource(Instance, resourceInfo);
if (!resourceHandle)
return NULL;
dialog = LockResource(resourceHandle);
if (!dialog)
return NULL;
dialogCopy = PhAllocateCopy(dialog, resourceSize);
if (dialogCopy->signature == 0xffff)
{
dialogCopy->style = Style;
}
else
{
((DLGTEMPLATE *)dialogCopy)->style = Style;
}
dialogHandle = CreateDialogIndirectParam(Instance, (DLGTEMPLATE *)dialogCopy, Parent, DialogProc, (LPARAM)Parameter);
PhFree(dialogCopy);
return dialogHandle;
}
BOOLEAN PhModalPropertySheet(
_Inout_ PROPSHEETHEADER *Header
)
{
// PropertySheet incorrectly discards WM_QUIT messages in certain cases, so we will use our own
// message loop. An example of this is when GetMessage (called by PropertySheet's message loop)
// dispatches a message directly from kernel-mode that causes the property sheet to close. In
// that case PropertySheet will retrieve the WM_QUIT message but will ignore it because of its
// buggy logic.
// This is also a good opportunity to introduce an auto-pool.
PH_AUTO_POOL autoPool;
HWND oldFocus;
HWND topLevelOwner;
HWND hwnd;
BOOL result;
MSG message;
PhInitializeAutoPool(&autoPool);
oldFocus = GetFocus();
topLevelOwner = Header->hwndParent;
while (topLevelOwner && (GetWindowLong(topLevelOwner, GWL_STYLE) & WS_CHILD))
topLevelOwner = GetParent(topLevelOwner);
if (topLevelOwner && (topLevelOwner == GetDesktopWindow() || EnableWindow(topLevelOwner, FALSE)))
topLevelOwner = NULL;
Header->dwFlags |= PSH_MODELESS;
hwnd = (HWND)PropertySheet(Header);
if (!hwnd)
{
if (topLevelOwner)
EnableWindow(topLevelOwner, TRUE);
return FALSE;
}
while (result = GetMessage(&message, NULL, 0, 0))
{
if (result == -1)
break;
if (!PropSheet_IsDialogMessage(hwnd, &message))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
PhDrainAutoPool(&autoPool);
// Destroy the window when necessary.
if (!PropSheet_GetCurrentPageHwnd(hwnd))
break;
}
if (result == 0)
PostQuitMessage((INT)message.wParam);
if (Header->hwndParent && GetActiveWindow() == hwnd)
SetActiveWindow(Header->hwndParent);
if (topLevelOwner)
EnableWindow(topLevelOwner, TRUE);
if (oldFocus && IsWindow(oldFocus))
SetFocus(oldFocus);
DestroyWindow(hwnd);
PhDeleteAutoPool(&autoPool);
return TRUE;
}
VOID PhInitializeLayoutManager(
_Out_ PPH_LAYOUT_MANAGER Manager,
_In_ HWND RootWindowHandle
)
{
Manager->List = PhCreateList(4);
Manager->LayoutNumber = 0;
Manager->RootItem.Handle = RootWindowHandle;
GetClientRect(Manager->RootItem.Handle, &Manager->RootItem.Rect);
Manager->RootItem.OrigRect = Manager->RootItem.Rect;
Manager->RootItem.ParentItem = NULL;
Manager->RootItem.LayoutParentItem = NULL;
Manager->RootItem.LayoutNumber = 0;
Manager->RootItem.NumberOfChildren = 0;
Manager->RootItem.DeferHandle = NULL;
}
VOID PhDeleteLayoutManager(
_Inout_ PPH_LAYOUT_MANAGER Manager
)
{
ULONG i;
for (i = 0; i < Manager->List->Count; i++)
PhFree(Manager->List->Items[i]);
PhDereferenceObject(Manager->List);
}
// HACK: The math below is all horribly broken, especially the HACK for multiline tab controls.
PPH_LAYOUT_ITEM PhAddLayoutItem(
_Inout_ PPH_LAYOUT_MANAGER Manager,
_In_ HWND Handle,
_In_opt_ PPH_LAYOUT_ITEM ParentItem,
_In_ ULONG Anchor
)
{
PPH_LAYOUT_ITEM layoutItem;
RECT dummy = { 0 };
layoutItem = PhAddLayoutItemEx(
Manager,
Handle,
ParentItem,
Anchor,
dummy
);
layoutItem->Margin = layoutItem->Rect;
PhConvertRect(&layoutItem->Margin, &layoutItem->ParentItem->Rect);
if (layoutItem->ParentItem != layoutItem->LayoutParentItem)
{
// Fix the margin because the item has a dummy parent. They share the same layout parent
// item.
layoutItem->Margin.top -= layoutItem->ParentItem->Rect.top;
layoutItem->Margin.left -= layoutItem->ParentItem->Rect.left;
layoutItem->Margin.right = layoutItem->ParentItem->Margin.right;
layoutItem->Margin.bottom = layoutItem->ParentItem->Margin.bottom;
}
return layoutItem;
}
PPH_LAYOUT_ITEM PhAddLayoutItemEx(
_Inout_ PPH_LAYOUT_MANAGER Manager,
_In_ HWND Handle,
_In_opt_ PPH_LAYOUT_ITEM ParentItem,
_In_ ULONG Anchor,
_In_ RECT Margin
)
{
PPH_LAYOUT_ITEM item;
if (!ParentItem)
ParentItem = &Manager->RootItem;
item = PhAllocate(sizeof(PH_LAYOUT_ITEM));
item->Handle = Handle;
item->ParentItem = ParentItem;
item->LayoutNumber = Manager->LayoutNumber;
item->NumberOfChildren = 0;
item->DeferHandle = NULL;
item->Anchor = Anchor;
item->LayoutParentItem = item->ParentItem;
while ((item->LayoutParentItem->Anchor & PH_LAYOUT_DUMMY_MASK) &&
item->LayoutParentItem->LayoutParentItem)
{
item->LayoutParentItem = item->LayoutParentItem->LayoutParentItem;
}
item->LayoutParentItem->NumberOfChildren++;
GetWindowRect(Handle, &item->Rect);
MapWindowPoints(NULL, item->LayoutParentItem->Handle, (POINT *)&item->Rect, 2);
if (item->Anchor & PH_LAYOUT_TAB_CONTROL)
{
// We want to convert the tab control rectangle to the tab page display rectangle.
TabCtrl_AdjustRect(Handle, FALSE, &item->Rect);
}
item->Margin = Margin;
item->OrigRect = item->Rect;
PhAddItemList(Manager->List, item);
return item;
}
VOID PhpLayoutItemLayout(
_Inout_ PPH_LAYOUT_MANAGER Manager,
_Inout_ PPH_LAYOUT_ITEM Item
)
{
RECT rect;
BOOLEAN hasDummyParent;
if (Item->NumberOfChildren > 0 && !Item->DeferHandle)
Item->DeferHandle = BeginDeferWindowPos(Item->NumberOfChildren);
if (Item->LayoutNumber == Manager->LayoutNumber)
return;
// If this is the root item we must stop here.
if (!Item->ParentItem)
return;
PhpLayoutItemLayout(Manager, Item->ParentItem);
if (Item->ParentItem != Item->LayoutParentItem)
{
PhpLayoutItemLayout(Manager, Item->LayoutParentItem);
hasDummyParent = TRUE;
}
else
{
hasDummyParent = FALSE;
}
GetWindowRect(Item->Handle, &Item->Rect);
MapWindowPoints(NULL, Item->LayoutParentItem->Handle, (POINT *)&Item->Rect, 2);
if (Item->Anchor & PH_LAYOUT_TAB_CONTROL)
{
// We want to convert the tab control rectangle to the tab page display rectangle.
TabCtrl_AdjustRect(Item->Handle, FALSE, &Item->Rect);
}
if (!(Item->Anchor & PH_LAYOUT_DUMMY_MASK))
{
// Convert right/bottom into margins to make the calculations
// easier.
rect = Item->Rect;
PhConvertRect(&rect, &Item->LayoutParentItem->Rect);
if (!(Item->Anchor & (PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT)))
{
// TODO
PhRaiseStatus(STATUS_NOT_IMPLEMENTED);
}
else if (Item->Anchor & PH_ANCHOR_RIGHT)
{
if (Item->Anchor & PH_ANCHOR_LEFT)
{
rect.left = (hasDummyParent ? Item->ParentItem->Rect.left : 0) + Item->Margin.left;
rect.right = Item->Margin.right;
}
else
{
ULONG diff = Item->Margin.right - rect.right;
rect.left -= diff;
rect.right += diff;
}
}
if (!(Item->Anchor & (PH_ANCHOR_TOP | PH_ANCHOR_BOTTOM)))
{
// TODO
PhRaiseStatus(STATUS_NOT_IMPLEMENTED);
}
else if (Item->Anchor & PH_ANCHOR_BOTTOM)
{
if (Item->Anchor & PH_ANCHOR_TOP)
{
// tab control hack
rect.top = (hasDummyParent ? Item->ParentItem->Rect.top : 0) + Item->Margin.top;
rect.bottom = Item->Margin.bottom;
}
else
{
ULONG diff = Item->Margin.bottom - rect.bottom;
rect.top -= diff;
rect.bottom += diff;
}
}
// Convert the right/bottom back into co-ordinates.
PhConvertRect(&rect, &Item->LayoutParentItem->Rect);
Item->Rect = rect;
if (!(Item->Anchor & PH_LAYOUT_IMMEDIATE_RESIZE))
{
Item->LayoutParentItem->DeferHandle = DeferWindowPos(
Item->LayoutParentItem->DeferHandle, Item->Handle,
NULL, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOZORDER
);
}
else
{
// This is needed for tab controls, so that TabCtrl_AdjustRect will give us an
// up-to-date result.
SetWindowPos(
Item->Handle,
NULL, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOZORDER
);
}
}
Item->LayoutNumber = Manager->LayoutNumber;
}
VOID PhLayoutManagerLayout(
_Inout_ PPH_LAYOUT_MANAGER Manager
)
{
ULONG i;
Manager->LayoutNumber++;
GetClientRect(Manager->RootItem.Handle, &Manager->RootItem.Rect);
for (i = 0; i < Manager->List->Count; i++)
{
PPH_LAYOUT_ITEM item = (PPH_LAYOUT_ITEM)Manager->List->Items[i];
PhpLayoutItemLayout(Manager, item);
}
for (i = 0; i < Manager->List->Count; i++)
{
PPH_LAYOUT_ITEM item = (PPH_LAYOUT_ITEM)Manager->List->Items[i];
if (item->DeferHandle)
{
EndDeferWindowPos(item->DeferHandle);
item->DeferHandle = NULL;
}
if (item->Anchor & PH_LAYOUT_FORCE_INVALIDATE)
{
InvalidateRect(item->Handle, NULL, FALSE);
}
}
if (Manager->RootItem.DeferHandle)
{
EndDeferWindowPos(Manager->RootItem.DeferHandle);
Manager->RootItem.DeferHandle = NULL;
}
}