/* * 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 . */ #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); }