462 lines
13 KiB
C
462 lines
13 KiB
C
/*
|
|
* Process Hacker Window Explorer -
|
|
* window treelist
|
|
*
|
|
* 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/>.
|
|
*/
|
|
|
|
#include "wndexp.h"
|
|
#include "resource.h"
|
|
|
|
BOOLEAN WepWindowNodeHashtableEqualFunction(
|
|
_In_ PVOID Entry1,
|
|
_In_ PVOID Entry2
|
|
);
|
|
|
|
ULONG WepWindowNodeHashtableHashFunction(
|
|
_In_ PVOID Entry
|
|
);
|
|
|
|
VOID WepDestroyWindowNode(
|
|
_In_ PWE_WINDOW_NODE WindowNode
|
|
);
|
|
|
|
BOOLEAN NTAPI WepWindowTreeNewCallback(
|
|
_In_ HWND hwnd,
|
|
_In_ PH_TREENEW_MESSAGE Message,
|
|
_In_opt_ PVOID Parameter1,
|
|
_In_opt_ PVOID Parameter2,
|
|
_In_opt_ PVOID Context
|
|
);
|
|
|
|
VOID WeInitializeWindowTree(
|
|
_In_ HWND ParentWindowHandle,
|
|
_In_ HWND TreeNewHandle,
|
|
_Out_ PWE_WINDOW_TREE_CONTEXT Context
|
|
)
|
|
{
|
|
HWND hwnd;
|
|
PPH_STRING settings;
|
|
|
|
memset(Context, 0, sizeof(WE_WINDOW_TREE_CONTEXT));
|
|
|
|
Context->NodeHashtable = PhCreateHashtable(
|
|
sizeof(PWE_WINDOW_NODE),
|
|
WepWindowNodeHashtableEqualFunction,
|
|
WepWindowNodeHashtableHashFunction,
|
|
100
|
|
);
|
|
Context->NodeList = PhCreateList(100);
|
|
Context->NodeRootList = PhCreateList(30);
|
|
|
|
Context->ParentWindowHandle = ParentWindowHandle;
|
|
Context->TreeNewHandle = TreeNewHandle;
|
|
hwnd = TreeNewHandle;
|
|
PhSetControlTheme(hwnd, L"explorer");
|
|
|
|
TreeNew_SetCallback(hwnd, WepWindowTreeNewCallback, Context);
|
|
|
|
PhAddTreeNewColumn(hwnd, WEWNTLC_CLASS, TRUE, L"Class", 180, PH_ALIGN_LEFT, 0, 0);
|
|
PhAddTreeNewColumn(hwnd, WEWNTLC_HANDLE, TRUE, L"Handle", 70, PH_ALIGN_LEFT, 1, 0);
|
|
PhAddTreeNewColumn(hwnd, WEWNTLC_TEXT, TRUE, L"Text", 220, PH_ALIGN_LEFT, 2, 0);
|
|
PhAddTreeNewColumn(hwnd, WEWNTLC_THREAD, TRUE, L"Thread", 150, PH_ALIGN_LEFT, 3, 0);
|
|
|
|
TreeNew_SetTriState(hwnd, TRUE);
|
|
TreeNew_SetSort(hwnd, 0, NoSortOrder);
|
|
|
|
settings = PhGetStringSetting(SETTING_NAME_WINDOW_TREE_LIST_COLUMNS);
|
|
PhCmLoadSettings(hwnd, &settings->sr);
|
|
PhDereferenceObject(settings);
|
|
}
|
|
|
|
VOID WeDeleteWindowTree(
|
|
_In_ PWE_WINDOW_TREE_CONTEXT Context
|
|
)
|
|
{
|
|
PPH_STRING settings;
|
|
ULONG i;
|
|
|
|
settings = PhCmSaveSettings(Context->TreeNewHandle);
|
|
PhSetStringSetting2(SETTING_NAME_WINDOW_TREE_LIST_COLUMNS, &settings->sr);
|
|
PhDereferenceObject(settings);
|
|
|
|
for (i = 0; i < Context->NodeList->Count; i++)
|
|
WepDestroyWindowNode(Context->NodeList->Items[i]);
|
|
|
|
PhDereferenceObject(Context->NodeHashtable);
|
|
PhDereferenceObject(Context->NodeList);
|
|
PhDereferenceObject(Context->NodeRootList);
|
|
}
|
|
|
|
BOOLEAN WepWindowNodeHashtableEqualFunction(
|
|
_In_ PVOID Entry1,
|
|
_In_ PVOID Entry2
|
|
)
|
|
{
|
|
PWE_WINDOW_NODE windowNode1 = *(PWE_WINDOW_NODE *)Entry1;
|
|
PWE_WINDOW_NODE windowNode2 = *(PWE_WINDOW_NODE *)Entry2;
|
|
|
|
return windowNode1->WindowHandle == windowNode2->WindowHandle;
|
|
}
|
|
|
|
ULONG WepWindowNodeHashtableHashFunction(
|
|
_In_ PVOID Entry
|
|
)
|
|
{
|
|
return PhHashIntPtr((ULONG_PTR)(*(PWE_WINDOW_NODE *)Entry)->WindowHandle);
|
|
}
|
|
|
|
PWE_WINDOW_NODE WeAddWindowNode(
|
|
_Inout_ PWE_WINDOW_TREE_CONTEXT Context
|
|
)
|
|
{
|
|
PWE_WINDOW_NODE windowNode;
|
|
|
|
windowNode = PhAllocate(sizeof(WE_WINDOW_NODE));
|
|
memset(windowNode, 0, sizeof(WE_WINDOW_NODE));
|
|
PhInitializeTreeNewNode(&windowNode->Node);
|
|
|
|
memset(windowNode->TextCache, 0, sizeof(PH_STRINGREF) * WEWNTLC_MAXIMUM);
|
|
windowNode->Node.TextCache = windowNode->TextCache;
|
|
windowNode->Node.TextCacheSize = WEWNTLC_MAXIMUM;
|
|
|
|
windowNode->Children = PhCreateList(1);
|
|
|
|
PhAddEntryHashtable(Context->NodeHashtable, &windowNode);
|
|
PhAddItemList(Context->NodeList, windowNode);
|
|
|
|
TreeNew_NodesStructured(Context->TreeNewHandle);
|
|
|
|
return windowNode;
|
|
}
|
|
|
|
PWE_WINDOW_NODE WeFindWindowNode(
|
|
_In_ PWE_WINDOW_TREE_CONTEXT Context,
|
|
_In_ HWND WindowHandle
|
|
)
|
|
{
|
|
WE_WINDOW_NODE lookupWindowNode;
|
|
PWE_WINDOW_NODE lookupWindowNodePtr = &lookupWindowNode;
|
|
PWE_WINDOW_NODE *windowNode;
|
|
|
|
lookupWindowNode.WindowHandle = WindowHandle;
|
|
|
|
windowNode = (PWE_WINDOW_NODE *)PhFindEntryHashtable(
|
|
Context->NodeHashtable,
|
|
&lookupWindowNodePtr
|
|
);
|
|
|
|
if (windowNode)
|
|
return *windowNode;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
VOID WeRemoveWindowNode(
|
|
_In_ PWE_WINDOW_TREE_CONTEXT Context,
|
|
_In_ PWE_WINDOW_NODE WindowNode
|
|
)
|
|
{
|
|
ULONG index;
|
|
|
|
// Remove from hashtable/list and cleanup.
|
|
|
|
PhRemoveEntryHashtable(Context->NodeHashtable, &WindowNode);
|
|
|
|
if ((index = PhFindItemList(Context->NodeList, WindowNode)) != -1)
|
|
PhRemoveItemList(Context->NodeList, index);
|
|
|
|
WepDestroyWindowNode(WindowNode);
|
|
|
|
TreeNew_NodesStructured(Context->TreeNewHandle);
|
|
}
|
|
|
|
VOID WepDestroyWindowNode(
|
|
_In_ PWE_WINDOW_NODE WindowNode
|
|
)
|
|
{
|
|
PhDereferenceObject(WindowNode->Children);
|
|
|
|
if (WindowNode->WindowText) PhDereferenceObject(WindowNode->WindowText);
|
|
|
|
if (WindowNode->ThreadString) PhDereferenceObject(WindowNode->ThreadString);
|
|
|
|
PhFree(WindowNode);
|
|
}
|
|
|
|
#define SORT_FUNCTION(Column) WepWindowTreeNewCompare##Column
|
|
|
|
#define BEGIN_SORT_FUNCTION(Column) static int __cdecl WepWindowTreeNewCompare##Column( \
|
|
_In_ void *_context, \
|
|
_In_ const void *_elem1, \
|
|
_In_ const void *_elem2 \
|
|
) \
|
|
{ \
|
|
PWE_WINDOW_NODE node1 = *(PWE_WINDOW_NODE *)_elem1; \
|
|
PWE_WINDOW_NODE node2 = *(PWE_WINDOW_NODE *)_elem2; \
|
|
int sortResult = 0;
|
|
|
|
#define END_SORT_FUNCTION \
|
|
return PhModifySort(sortResult, ((PWE_WINDOW_TREE_CONTEXT)_context)->TreeNewSortOrder); \
|
|
}
|
|
|
|
BEGIN_SORT_FUNCTION(Class)
|
|
{
|
|
sortResult = _wcsicmp(node1->WindowClass, node2->WindowClass);
|
|
}
|
|
END_SORT_FUNCTION
|
|
|
|
BEGIN_SORT_FUNCTION(Handle)
|
|
{
|
|
sortResult = uintptrcmp((ULONG_PTR)node1->WindowHandle, (ULONG_PTR)node2->WindowHandle);
|
|
}
|
|
END_SORT_FUNCTION
|
|
|
|
BEGIN_SORT_FUNCTION(Text)
|
|
{
|
|
sortResult = PhCompareString(node1->WindowText, node2->WindowText, TRUE);
|
|
}
|
|
END_SORT_FUNCTION
|
|
|
|
BEGIN_SORT_FUNCTION(Thread)
|
|
{
|
|
sortResult = uintptrcmp((ULONG_PTR)node1->ClientId.UniqueProcess, (ULONG_PTR)node2->ClientId.UniqueProcess);
|
|
|
|
if (sortResult == 0)
|
|
sortResult = uintptrcmp((ULONG_PTR)node1->ClientId.UniqueThread, (ULONG_PTR)node2->ClientId.UniqueThread);
|
|
}
|
|
END_SORT_FUNCTION
|
|
|
|
BOOLEAN NTAPI WepWindowTreeNewCallback(
|
|
_In_ HWND hwnd,
|
|
_In_ PH_TREENEW_MESSAGE Message,
|
|
_In_opt_ PVOID Parameter1,
|
|
_In_opt_ PVOID Parameter2,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PWE_WINDOW_TREE_CONTEXT context;
|
|
PWE_WINDOW_NODE node;
|
|
|
|
context = Context;
|
|
|
|
switch (Message)
|
|
{
|
|
case TreeNewGetChildren:
|
|
{
|
|
PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
|
|
|
|
node = (PWE_WINDOW_NODE)getChildren->Node;
|
|
|
|
if (context->TreeNewSortOrder == NoSortOrder)
|
|
{
|
|
if (!node)
|
|
{
|
|
getChildren->Children = (PPH_TREENEW_NODE *)context->NodeRootList->Items;
|
|
getChildren->NumberOfChildren = context->NodeRootList->Count;
|
|
}
|
|
else
|
|
{
|
|
getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items;
|
|
getChildren->NumberOfChildren = node->Children->Count;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!node)
|
|
{
|
|
static PVOID sortFunctions[] =
|
|
{
|
|
SORT_FUNCTION(Class),
|
|
SORT_FUNCTION(Handle),
|
|
SORT_FUNCTION(Text),
|
|
SORT_FUNCTION(Thread)
|
|
};
|
|
int (__cdecl *sortFunction)(void *, const void *, const void *);
|
|
|
|
if (context->TreeNewSortColumn < WEWNTLC_MAXIMUM)
|
|
sortFunction = sortFunctions[context->TreeNewSortColumn];
|
|
else
|
|
sortFunction = NULL;
|
|
|
|
if (sortFunction)
|
|
{
|
|
qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context);
|
|
}
|
|
|
|
getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items;
|
|
getChildren->NumberOfChildren = context->NodeList->Count;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
case TreeNewIsLeaf:
|
|
{
|
|
PPH_TREENEW_IS_LEAF isLeaf = Parameter1;
|
|
|
|
node = (PWE_WINDOW_NODE)isLeaf->Node;
|
|
|
|
if (context->TreeNewSortOrder == NoSortOrder)
|
|
isLeaf->IsLeaf = !node->HasChildren;
|
|
else
|
|
isLeaf->IsLeaf = TRUE;
|
|
}
|
|
return TRUE;
|
|
case TreeNewGetCellText:
|
|
{
|
|
PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1;
|
|
|
|
node = (PWE_WINDOW_NODE)getCellText->Node;
|
|
|
|
switch (getCellText->Id)
|
|
{
|
|
case WEWNTLC_CLASS:
|
|
PhInitializeStringRef(&getCellText->Text, node->WindowClass);
|
|
break;
|
|
case WEWNTLC_HANDLE:
|
|
PhPrintPointer(node->WindowHandleString, node->WindowHandle);
|
|
PhInitializeStringRef(&getCellText->Text, node->WindowHandleString);
|
|
break;
|
|
case WEWNTLC_TEXT:
|
|
getCellText->Text = PhGetStringRef(node->WindowText);
|
|
break;
|
|
case WEWNTLC_THREAD:
|
|
if (!node->ThreadString)
|
|
node->ThreadString = PhGetClientIdName(&node->ClientId);
|
|
getCellText->Text = PhGetStringRef(node->ThreadString);
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
getCellText->Flags = TN_CACHE;
|
|
}
|
|
return TRUE;
|
|
case TreeNewGetNodeColor:
|
|
{
|
|
PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1;
|
|
|
|
node = (PWE_WINDOW_NODE)getNodeColor->Node;
|
|
|
|
if (!node->WindowVisible)
|
|
getNodeColor->ForeColor = RGB(0x55, 0x55, 0x55);
|
|
|
|
getNodeColor->Flags = TN_CACHE;
|
|
}
|
|
return TRUE;
|
|
case TreeNewSortChanged:
|
|
{
|
|
TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder);
|
|
// Force a rebuild to sort the items.
|
|
TreeNew_NodesStructured(hwnd);
|
|
}
|
|
return TRUE;
|
|
case TreeNewKeyDown:
|
|
{
|
|
PPH_TREENEW_KEY_EVENT keyEvent = Parameter1;
|
|
|
|
switch (keyEvent->VirtualKey)
|
|
{
|
|
case 'C':
|
|
if (GetKeyState(VK_CONTROL) < 0)
|
|
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WINDOW_COPY, 0);
|
|
break;
|
|
}
|
|
}
|
|
return TRUE;
|
|
case TreeNewLeftDoubleClick:
|
|
{
|
|
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WINDOW_PROPERTIES, 0);
|
|
}
|
|
return TRUE;
|
|
case TreeNewNodeExpanding:
|
|
{
|
|
SendMessage(context->ParentWindowHandle, WM_WE_PLUSMINUS, 0, (LPARAM)Parameter1);
|
|
}
|
|
return FALSE;
|
|
case TreeNewContextMenu:
|
|
{
|
|
PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1;
|
|
|
|
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, MAKELONG(mouseEvent->Location.x, mouseEvent->Location.y));
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID WeClearWindowTree(
|
|
_In_ PWE_WINDOW_TREE_CONTEXT Context
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < Context->NodeList->Count; i++)
|
|
WepDestroyWindowNode(Context->NodeList->Items[i]);
|
|
|
|
PhClearHashtable(Context->NodeHashtable);
|
|
PhClearList(Context->NodeList);
|
|
PhClearList(Context->NodeRootList);
|
|
}
|
|
|
|
PWE_WINDOW_NODE WeGetSelectedWindowNode(
|
|
_In_ PWE_WINDOW_TREE_CONTEXT Context
|
|
)
|
|
{
|
|
PWE_WINDOW_NODE windowNode = NULL;
|
|
ULONG i;
|
|
|
|
for (i = 0; i < Context->NodeList->Count; i++)
|
|
{
|
|
windowNode = Context->NodeList->Items[i];
|
|
|
|
if (windowNode->Node.Selected)
|
|
return windowNode;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID WeGetSelectedWindowNodes(
|
|
_In_ PWE_WINDOW_TREE_CONTEXT Context,
|
|
_Out_ PWE_WINDOW_NODE **Windows,
|
|
_Out_ PULONG NumberOfWindows
|
|
)
|
|
{
|
|
PPH_LIST list;
|
|
ULONG i;
|
|
|
|
list = PhCreateList(2);
|
|
|
|
for (i = 0; i < Context->NodeList->Count; i++)
|
|
{
|
|
PWE_WINDOW_NODE node = Context->NodeList->Items[i];
|
|
|
|
if (node->Node.Selected)
|
|
{
|
|
PhAddItemList(list, node);
|
|
}
|
|
}
|
|
|
|
*Windows = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
|
|
*NumberOfWindows = list->Count;
|
|
|
|
PhDereferenceObject(list);
|
|
}
|