add plugins-extra

This commit is contained in:
AirDog46
2025-05-13 19:49:49 +03:00
parent c5fab8aa94
commit 3575d86c17
531 changed files with 70258 additions and 1 deletions

View File

@@ -0,0 +1,2 @@
1.0
* Initial release

View File

@@ -0,0 +1,135 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (Australia) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "0c0904b0"
BEGIN
VALUE "CompanyName", "dmex"
VALUE "FileDescription", "Wait Chain Traversal plugin for Process Hacker"
VALUE "FileVersion", "1.0"
VALUE "InternalName", "dmex.WaitChainPlugin"
VALUE "LegalCopyright", "Licensed under the GNU GPL, v3."
VALUE "OriginalFilename", "WaitChainPlugin.dll"
VALUE "ProductName", "Wait Chain Traversal plugin for Process Hacker"
VALUE "ProductVersion", "1.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0xc09, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_WCT_DIALOG DIALOGEX 0, 0, 333, 180
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "Wait Chain Traversal"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "Windows",IDC_CUSTOM1,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0x2,7,5,319,150,WS_EX_CLIENTEDGE
DEFPUSHBUTTON "Close",IDOK,277,160,50,14
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_WCT_DIALOG, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 326
TOPMARGIN, 5
BOTTOMMARGIN, 173
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MAIN_MENU MENU
BEGIN
POPUP "Menu"
BEGIN
MENUITEM "Go to Process...", ID_MENU_GOTOPROCESS
MENUITEM "Go to Thread...", ID_MENU_GOTOTHREAD
END
END
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\0"
END
3 TEXTINCLUDE
BEGIN
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (Australia) resources
/////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{D6EA1C23-4CBC-4CD5-931C-38C984722510}</ProjectGuid>
<RootNamespace>WaitChainPlugin</RootNamespace>
<Keyword>Win32Proj</Keyword>
<ProjectName>WaitChainPlugin</ProjectName>
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\ExtraPlugins.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\ExtraPlugins.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\ExtraPlugins.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\ExtraPlugins.props" />
</ImportGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LibraryPath>$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(NETFXKitsDir)Lib\um\x86;C:\Users\AirDog46\Downloads\processhacker-2.39-src\bin\Debug32</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LibraryPath>$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64;C:\Users\AirDog46\Downloads\processhacker-2.39-src\bin\Release64</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LibraryPath>$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(NETFXKitsDir)Lib\um\x86;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(NETFXKitsDir)Lib\um\x86;C:\Users\AirDog46\Downloads\processhacker-2.39-src\bin\Release32</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<TreatWarningAsError>false</TreatWarningAsError>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<TreatWarningAsError>false</TreatWarningAsError>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<TreatWarningAsError>false</TreatWarningAsError>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<TreatWarningAsError>false</TreatWarningAsError>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.c" />
<ClCompile Include="wndtree.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="main.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="wndtree.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="WaitChainPlugin.rc" />
</ItemGroup>
<ItemGroup>
<Text Include="CHANGELOG.txt" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{802108be-ae96-47c3-8d93-884ed6dd096a}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{3e65ffb8-3f3e-40d7-b3ca-d55cae8edb16}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{b98dd849-bbfe-4b41-8c48-f65680da5c00}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="wndtree.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="wndtree.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="WaitChainPlugin.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Text Include="CHANGELOG.txt" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,532 @@
/*
* Process Hacker Extra Plugins -
* Wait Chain Traversal (WCT) Plugin
*
* Copyright (C) 2013-2015 dmex
*
* 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 "main.h"
// Wait Chain Traversal Documentation:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms681622.aspx
static PPH_PLUGIN PluginInstance;
static PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration;
static PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration;
static PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration;
BOOLEAN WaitChainRegisterCallbacks(
_Inout_ PWCT_CONTEXT Context
)
{
PCOGETCALLSTATE coGetCallStateCallback = NULL;
PCOGETACTIVATIONSTATE coGetActivationStateCallback = NULL;
if (!(Context->Ole32ModuleHandle = LoadLibrary(L"ole32.dll")))
return FALSE;
if (!(coGetCallStateCallback = PhGetProcedureAddress(Context->Ole32ModuleHandle, "CoGetCallState", 0)))
return FALSE;
if (!(coGetActivationStateCallback = PhGetProcedureAddress(Context->Ole32ModuleHandle, "CoGetActivationState", 0)))
return FALSE;
RegisterWaitChainCOMCallback(coGetCallStateCallback, coGetActivationStateCallback);
return TRUE;
}
VOID WaitChainCheckThread(
_Inout_ PWCT_CONTEXT Context,
_In_ HANDLE ThreadId
)
{
BOOL isDeadLocked = FALSE;
ULONG nodeInfoLength = WCT_MAX_NODE_COUNT;
WAITCHAIN_NODE_INFO nodeInfoArray[WCT_MAX_NODE_COUNT];
PWCT_ROOT_NODE rootNode = NULL;
memset(nodeInfoArray, 0, sizeof(nodeInfoArray));
// Retrieve the thread wait chain.
if (!GetThreadWaitChain(
Context->WctSessionHandle,
0,
WCT_GETINFO_ALL_FLAGS,
HandleToUlong(ThreadId),
&nodeInfoLength,
nodeInfoArray,
&isDeadLocked
))
{
return;
}
// Check if the wait chain is too big for the array we passed in.
if (nodeInfoLength > WCT_MAX_NODE_COUNT)
nodeInfoLength = WCT_MAX_NODE_COUNT;
for (ULONG i = 0; i < nodeInfoLength; i++)
{
PWAITCHAIN_NODE_INFO wctNode = &nodeInfoArray[i];
if (wctNode->ObjectType == WctThreadType)
{
rootNode = WeAddWindowNode(&Context->TreeContext);
rootNode->ObjectType = wctNode->ObjectType;
rootNode->ObjectStatus = wctNode->ObjectStatus;
rootNode->Alertable = wctNode->LockObject.Alertable;
rootNode->ThreadId = UlongToHandle(wctNode->ThreadObject.ThreadId);
rootNode->ProcessId = UlongToHandle(wctNode->ThreadObject.ProcessId);
rootNode->ThreadIdString = PhFormatString(L"%lu", wctNode->ThreadObject.ThreadId);
rootNode->ProcessIdString = PhFormatString(L"%lu", wctNode->ThreadObject.ProcessId);
rootNode->WaitTimeString = PhFormatString(L"%lu", wctNode->ThreadObject.WaitTime);
rootNode->ContextSwitchesString = PhFormatString(L"%lu", wctNode->ThreadObject.ContextSwitches);
rootNode->TimeoutString = PhFormatString(L"%I64d", wctNode->LockObject.Timeout.QuadPart);
if (wctNode->LockObject.ObjectName[0] != '\0')
{
// -- ProcessID --
//wctNode->LockObject.ObjectName[0]
// -- ThreadID --
//wctNode->LockObject.ObjectName[2]
// -- Unknown --
//wctNode->LockObject.ObjectName[4]
// -- ContextSwitches --
//wctNode->LockObject.ObjectName[6]
if (PhIsDigitCharacter(wctNode->LockObject.ObjectName[0]))
{
rootNode->ObjectNameString = PhFormatString(L"%s", wctNode->LockObject.ObjectName);
}
//else
//{
// rootNode->ObjectNameString = PhFormatString(L"[%lu, %lu]",
// wctNode.LockObject.ObjectName[0],
// wctNode.LockObject.ObjectName[2]
// );
//}
}
rootNode->Node.Expanded = TRUE;
rootNode->HasChildren = TRUE;
// This is a root node.
PhAddItemList(Context->TreeContext.NodeRootList, rootNode);
}
else
{
WctAddChildWindowNode(&Context->TreeContext, rootNode, wctNode, isDeadLocked);
}
}
}
NTSTATUS WaitChainCallbackThread(
_In_ PVOID Parameter
)
{
NTSTATUS status = STATUS_SUCCESS;
PWCT_CONTEXT context = (PWCT_CONTEXT)Parameter;
if (!WaitChainRegisterCallbacks(context))
return NTSTATUS_FROM_WIN32(GetLastError());
// Synchronous WCT session
if (!(context->WctSessionHandle = OpenThreadWaitChainSession(0, NULL)))
return NTSTATUS_FROM_WIN32(GetLastError());
//TreeNew_SetRedraw(context->TreeNewHandle, FALSE);
if (context->IsProcessItem)
{
NTSTATUS status;
HANDLE threadHandle;
HANDLE newThreadHandle;
THREAD_BASIC_INFORMATION basicInfo;
status = NtGetNextThread(
context->ProcessItem->QueryHandle,
NULL,
ThreadQueryAccess,
0,
0,
&threadHandle
);
while (NT_SUCCESS(status))
{
if (NT_SUCCESS(PhGetThreadBasicInformation(threadHandle, &basicInfo)))
{
WaitChainCheckThread(context, basicInfo.ClientId.UniqueThread);
}
status = NtGetNextThread(
context->ProcessItem->QueryHandle,
threadHandle,
ThreadQueryAccess,
0,
0,
&newThreadHandle
);
NtClose(threadHandle);
threadHandle = newThreadHandle;
}
}
else
{
WaitChainCheckThread(context, context->ThreadItem->ThreadId);
}
if (context->WctSessionHandle)
{
CloseThreadWaitChainSession(context->WctSessionHandle);
}
if (context->Ole32ModuleHandle)
{
FreeLibrary(context->Ole32ModuleHandle);
}
//TreeNew_SetRedraw(context->TreeNewHandle, TRUE);
TreeNew_NodesStructured(context->TreeNewHandle);
return status;
}
INT_PTR CALLBACK WaitChainDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PWCT_CONTEXT context = NULL;
if (uMsg == WM_INITDIALOG)
{
context = (PWCT_CONTEXT)lParam;
SetProp(hwndDlg, L"Context", (HANDLE)context);
}
else
{
context = (PWCT_CONTEXT)GetProp(hwndDlg, L"Context");
if (uMsg == WM_DESTROY)
{
PhUnregisterDialog(hwndDlg);
PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg);
PhDeleteLayoutManager(&context->LayoutManager);
WtcDeleteWindowTree(&context->TreeContext);
RemoveProp(hwndDlg, L"Context");
PhFree(context);
}
}
if (context == NULL)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
HANDLE threadHandle = NULL;
context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_CUSTOM1);
PhRegisterDialog(hwndDlg);
WtcInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT);
PhLoadWindowPlacementFromSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg);
if (threadHandle = PhCreateThread(0, WaitChainCallbackThread, (PVOID)context))
NtClose(threadHandle);
}
break;
case WM_SIZE:
PhLayoutManagerLayout(&context->LayoutManager);
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDCANCEL:
case IDOK:
EndDialog(hwndDlg, IDOK);
break;
case ID_WCTSHOWCONTEXTMENU:
{
POINT point;
PPH_EMENU menu;
PWCT_ROOT_NODE selectedNode;
point.x = (SHORT)LOWORD(lParam);
point.y = (SHORT)HIWORD(lParam);
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
menu = PhCreateEMenu();
PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_MAIN_MENU), 0);
PhSetFlagsEMenuItem(menu, ID_MENU_PROPERTIES, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT);
if (selectedNode->ThreadId > 0)
{
PhSetFlagsEMenuItem(menu, ID_MENU_GOTOTHREAD, PH_EMENU_DISABLED, 0);
PhSetFlagsEMenuItem(menu, ID_MENU_GOTOPROCESS, PH_EMENU_DISABLED, 0);
}
else
{
PhSetFlagsEMenuItem(menu, ID_MENU_GOTOTHREAD, PH_EMENU_DISABLED, PH_EMENU_DISABLED);
PhSetFlagsEMenuItem(menu, ID_MENU_GOTOPROCESS, PH_EMENU_DISABLED, PH_EMENU_DISABLED);
}
PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y);
PhDestroyEMenu(menu);
}
}
break;
case ID_MENU_GOTOPROCESS:
{
PWCT_ROOT_NODE selectedNode = NULL;
PPH_PROCESS_NODE processNode = NULL;
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
if (processNode = PhFindProcessNode(selectedNode->ProcessId))
{
ProcessHacker_SelectTabPage(PhMainWndHandle, 0);
PhSelectAndEnsureVisibleProcessNode(processNode);
}
}
}
break;
case ID_MENU_GOTOTHREAD:
{
PWCT_ROOT_NODE selectedNode = NULL;
PPH_PROCESS_ITEM processItem = NULL;
PPH_PROCESS_PROPCONTEXT propContext = NULL;
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
if (processItem = PhReferenceProcessItem(selectedNode->ProcessId))
{
if (propContext = PhCreateProcessPropContext(NULL, processItem))
{
if (selectedNode->ThreadId)
{
PhSetSelectThreadIdProcessPropContext(propContext, selectedNode->ThreadId);
}
PhShowProcessProperties(propContext);
PhDereferenceObject(propContext);
}
PhDereferenceObject(processItem);
}
else
{
PhShowError(hwndDlg, L"The process does not exist.");
}
}
}
break;
case ID_MENU_COPY:
{
PPH_STRING text;
text = PhGetTreeNewText(context->TreeNewHandle, 0);
PhSetClipboardString(hwndDlg, &text->sr);
PhDereferenceObject(text);
}
break;
}
}
break;
}
return FALSE;
}
VOID NTAPI MenuItemCallback(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
PPH_PLUGIN_MENU_ITEM menuItem = (PPH_PLUGIN_MENU_ITEM)Parameter;
switch (menuItem->Id)
{
case IDD_WCT_MENUITEM:
{
DialogBoxParam(
PluginInstance->DllBase,
MAKEINTRESOURCE(IDD_WCT_DIALOG),
NULL,
WaitChainDlgProc,
(LPARAM)menuItem->Context
);
}
break;
}
}
VOID NTAPI ProcessMenuInitializingCallback(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
ULONG insertIndex = 0;
PWCT_CONTEXT context = NULL;
PPH_PLUGIN_MENU_INFORMATION menuInfo = NULL;
PPH_PROCESS_ITEM processItem = NULL;
PPH_EMENU_ITEM menuItem = NULL;
PPH_EMENU_ITEM miscMenuItem = NULL;
PPH_EMENU_ITEM wsMenuItem = NULL;
menuInfo = (PPH_PLUGIN_MENU_INFORMATION)Parameter;
if (menuInfo->u.Process.NumberOfProcesses == 1)
processItem = menuInfo->u.Process.Processes[0];
else
{
processItem = NULL;
}
if (processItem == NULL)
return;
context = (PWCT_CONTEXT)PhAllocate(sizeof(WCT_CONTEXT));
memset(context, 0, sizeof(WCT_CONTEXT));
context->IsProcessItem = TRUE;
context->ProcessItem = processItem;
if (miscMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Miscellaneous", 0))
{
PhInsertEMenuItem(miscMenuItem, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, IDD_WCT_MENUITEM, L"Wait Chain Traversal", context), -1);
if (!processItem || !processItem->QueryHandle || processItem->ProcessId == NtCurrentProcessId())
menuItem->Flags |= PH_EMENU_DISABLED;
if (!PhGetOwnTokenAttributes().Elevated)
menuItem->Flags |= PH_EMENU_DISABLED;
}
else
{
PhFree(context);
}
}
VOID NTAPI ThreadMenuInitializingCallback(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
PWCT_CONTEXT context = NULL;
PPH_PLUGIN_MENU_INFORMATION menuInfo = NULL;
PPH_THREAD_ITEM threadItem = NULL;
PPH_EMENU_ITEM menuItem = NULL;
PPH_EMENU_ITEM miscMenuItem = NULL;
menuInfo = (PPH_PLUGIN_MENU_INFORMATION)Parameter;
if (menuInfo->u.Thread.NumberOfThreads == 1)
threadItem = menuInfo->u.Thread.Threads[0];
else
threadItem = NULL;
context = (PWCT_CONTEXT)PhAllocate(sizeof(WCT_CONTEXT));
memset(context, 0, sizeof(WCT_CONTEXT));
context->IsProcessItem = FALSE;
context->ThreadItem = threadItem;
miscMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Analyze", 0);
if (miscMenuItem)
{
menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, IDD_WCT_MENUITEM, L"Wait Chain Traversal", context);
PhInsertEMenuItem(miscMenuItem, menuItem, -1);
// Disable menu if current process selected.
if (threadItem == NULL || menuInfo->u.Thread.ProcessId == NtCurrentProcessId())
menuItem->Flags |= PH_EMENU_DISABLED;
}
}
LOGICAL DllMain(
_In_ HINSTANCE Instance,
_In_ ULONG Reason,
_Reserved_ PVOID Reserved
)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH:
{
PPH_PLUGIN_INFORMATION info;
PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info);
if (!PluginInstance)
return FALSE;
info->DisplayName = L"Wait Chain Traversal";
info->Author = L"dmex";
info->Description = L"Plugin for Wait Chain analysis via right-click Miscellaneous > Wait Chain Traversal or individual threads via Process properties > Threads tab > Analyze > Wait Chain Traversal.";
info->HasOptions = FALSE;
PhRegisterCallback(
PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem),
MenuItemCallback,
NULL,
&PluginMenuItemCallbackRegistration
);
PhRegisterCallback(
PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing),
ProcessMenuInitializingCallback,
NULL,
&ProcessMenuInitializingCallbackRegistration
);
PhRegisterCallback(
PhGetGeneralCallback(GeneralCallbackThreadMenuInitializing),
ThreadMenuInitializingCallback,
NULL,
&ThreadMenuInitializingCallbackRegistration
);
{
PH_SETTING_CREATE settings[] =
{
{ StringSettingType, SETTING_NAME_TREE_LIST_COLUMNS, L"" },
{ IntegerPairSettingType, SETTING_NAME_WINDOW_POSITION, L"100,100" },
{ ScalableIntegerPairSettingType, SETTING_NAME_WINDOW_SIZE, L"@96|690,540" }
};
PhAddSettings(settings, ARRAYSIZE(settings));
}
}
break;
}
return TRUE;
}

View File

@@ -0,0 +1,65 @@
/*
* Process Hacker Extra Plugins -
* Wait Chain Traversal (WCT) Plugin
*
* Copyright (C) 2014 dmex
*
* 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/>.
*/
#ifndef _WCT_H_
#define _WCT_H_
#define CINTERFACE
#define COBJMACROS
#include <phdk.h>
#include <phappresource.h>
#include <wct.h>
#include <psapi.h>
#include "resource.h"
#include "wndtree.h"
#define IDD_WCT_MENUITEM 1000
#define WCT_GETINFO_ALL_FLAGS (WCT_OUT_OF_PROC_FLAG|WCT_OUT_OF_PROC_COM_FLAG|WCT_OUT_OF_PROC_CS_FLAG|WCT_NETWORK_IO_FLAG)
typedef struct _WCT_CONTEXT
{
HWND DialogHandle;
HWND TreeNewHandle;
HWND HighlightingWindow;
ULONG HighlightingWindowCount;
WCT_TREE_CONTEXT TreeContext;
PH_LAYOUT_MANAGER LayoutManager;
BOOLEAN IsProcessItem;
PPH_THREAD_ITEM ThreadItem;
PPH_PROCESS_ITEM ProcessItem;
HWCT WctSessionHandle;
HMODULE Ole32ModuleHandle;
} WCT_CONTEXT, *PWCT_CONTEXT;
BOOLEAN WaitChainRegisterCallbacks(
_Inout_ PWCT_CONTEXT Context
);
VOID WaitChainCheckThread(
_Inout_ PWCT_CONTEXT Context,
_In_ HANDLE ThreadId
);
#endif

View File

@@ -0,0 +1,23 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by WaitChainPlugin.rc
//
#define IDD_WCT_DIALOG 101
#define IDR_MAIN_MENU 111
#define IDC_CUSTOM1 1023
#define ID_DNSENTRY_FLUSH 40004
#define ID_MENU_GOTOTHREAD 40006
#define ID_MENU_PROPERTIES 40007
#define ID_MENU_COPY 40008
#define ID_MENU_GOTOPROCESS 40009
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 112
#define _APS_NEXT_COMMAND_VALUE 40010
#define _APS_NEXT_CONTROL_VALUE 1024
#define _APS_NEXT_SYMED_VALUE 108
#endif
#endif

View File

@@ -0,0 +1,584 @@
/*
* Process Hacker Extra Plugins -
* Wait Chain Traversal (WCT) Plugin
*
* Copyright (C) 2014 dmex
*
* 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 "main.h"
BOOLEAN NTAPI WepWindowTreeNewCallback(
_In_ HWND hwnd,
_In_ PH_TREENEW_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2,
_In_opt_ PVOID Context
);
BOOLEAN WepWindowNodeHashtableEqualFunction(
_In_ PVOID Entry1,
_In_ PVOID Entry2
)
{
PWCT_ROOT_NODE windowNode1 = *(PWCT_ROOT_NODE *)Entry1;
PWCT_ROOT_NODE windowNode2 = *(PWCT_ROOT_NODE *)Entry2;
return windowNode1->Node.Index == windowNode2->Node.Index;
}
ULONG WepWindowNodeHashtableHashFunction(
_In_ PVOID Entry
)
{
return (*(PWCT_ROOT_NODE*)Entry)->Node.Index;
}
VOID WepDestroyWindowNode(
_In_ PWCT_ROOT_NODE WindowNode
)
{
PhDereferenceObject(WindowNode->Children);
if (WindowNode->TimeoutString)
PhDereferenceObject(WindowNode->TimeoutString);
if (WindowNode->ProcessIdString)
PhDereferenceObject(WindowNode->ProcessIdString);
if (WindowNode->ThreadIdString)
PhDereferenceObject(WindowNode->ThreadIdString);
if (WindowNode->WaitTimeString)
PhDereferenceObject(WindowNode->WaitTimeString);
if (WindowNode->ContextSwitchesString)
PhDereferenceObject(WindowNode->ContextSwitchesString);
if (WindowNode->ObjectNameString)
PhDereferenceObject(WindowNode->ObjectNameString);
PhFree(WindowNode);
}
VOID WtcInitializeWindowTree(
_In_ HWND ParentWindowHandle,
_In_ HWND TreeNewHandle,
_Out_ PWCT_TREE_CONTEXT Context
)
{
HWND hwnd;
PPH_STRING settings;
memset(Context, 0, sizeof(WCT_TREE_CONTEXT));
Context->NodeHashtable = PhCreateHashtable(
sizeof(PWCT_ROOT_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, TREE_COLUMN_ITEM_TYPE, TRUE, L"Type", 80, PH_ALIGN_LEFT, 0, 0);
PhAddTreeNewColumn(hwnd, TREE_COLUMN_ITEM_THREADID, TRUE, L"ThreadId", 50, PH_ALIGN_LEFT, 1, 0);
PhAddTreeNewColumn(hwnd, TREE_COLUMN_ITEM_PROCESSID, TRUE, L"ProcessId", 50, PH_ALIGN_LEFT, 2, 0);
PhAddTreeNewColumn(hwnd, TREE_COLUMN_ITEM_STATUS, TRUE, L"Status", 80, PH_ALIGN_LEFT, 3, 0);
PhAddTreeNewColumn(hwnd, TREE_COLUMN_ITEM_CONTEXTSWITCH, TRUE, L"Context Switches", 70, PH_ALIGN_LEFT, 4, 0);
PhAddTreeNewColumn(hwnd, TREE_COLUMN_ITEM_WAITTIME, TRUE, L"WaitTime", 60, PH_ALIGN_LEFT, 5, 0);
PhAddTreeNewColumn(hwnd, TREE_COLUMN_ITEM_TIMEOUT, TRUE, L"Timeout", 60, PH_ALIGN_LEFT, 6, 0);
PhAddTreeNewColumn(hwnd, TREE_COLUMN_ITEM_ALERTABLE, TRUE, L"Alertable", 50, PH_ALIGN_LEFT, 7, 0);
PhAddTreeNewColumn(hwnd, TREE_COLUMN_ITEM_NAME, TRUE, L"Name", 100, PH_ALIGN_LEFT, 8, 0);
TreeNew_SetTriState(hwnd, TRUE);
TreeNew_SetSort(hwnd, 0, NoSortOrder);
settings = PhGetStringSetting(SETTING_NAME_TREE_LIST_COLUMNS);
PhCmLoadSettings(hwnd, &settings->sr);
PhDereferenceObject(settings);
}
VOID WtcDeleteWindowTree(
_In_ PWCT_TREE_CONTEXT Context
)
{
PPH_STRING settings;
settings = PhCmSaveSettings(Context->TreeNewHandle);
PhSetStringSetting2(SETTING_NAME_TREE_LIST_COLUMNS, &settings->sr);
PhDereferenceObject(settings);
for (ULONG i = 0; i < Context->NodeList->Count; i++)
{
WepDestroyWindowNode(Context->NodeList->Items[i]);
}
PhDereferenceObject(Context->NodeHashtable);
PhDereferenceObject(Context->NodeList);
PhDereferenceObject(Context->NodeRootList);
}
PWCT_ROOT_NODE WeAddWindowNode(
_Inout_ PWCT_TREE_CONTEXT Context
)
{
PWCT_ROOT_NODE windowNode;
windowNode = PhAllocate(sizeof(WCT_ROOT_NODE));
memset(windowNode, 0, sizeof(WCT_ROOT_NODE));
PhInitializeTreeNewNode(&windowNode->Node);
memset(windowNode->TextCache, 0, sizeof(PH_STRINGREF) * TREE_COLUMN_ITEM_MAXIMUM);
windowNode->Node.TextCache = windowNode->TextCache;
windowNode->Node.TextCacheSize = TREE_COLUMN_ITEM_MAXIMUM;
windowNode->Children = PhCreateList(1);
PhAddEntryHashtable(Context->NodeHashtable, &windowNode);
PhAddItemList(Context->NodeList, windowNode);
//TreeNew_NodesStructured(Context->TreeNewHandle);
return windowNode;
}
VOID WctAddChildWindowNode(
_In_ PWCT_TREE_CONTEXT Context,
_In_opt_ PWCT_ROOT_NODE ParentNode,
_In_ PWAITCHAIN_NODE_INFO WctNode,
_In_ BOOLEAN IsDeadLocked
)
{
PWCT_ROOT_NODE childNode = NULL;
childNode = WeAddWindowNode(Context);
childNode->IsDeadLocked = TRUE;
childNode->ObjectType = WctNode->ObjectType;
childNode->ObjectStatus = WctNode->ObjectStatus;
childNode->Alertable = WctNode->LockObject.Alertable;
childNode->ThreadId = UlongToHandle(WctNode->ThreadObject.ThreadId);
childNode->ProcessIdString = PhFormatString(L"%lu", WctNode->ThreadObject.ProcessId);
childNode->ThreadIdString = PhFormatString(L"%lu", WctNode->ThreadObject.ThreadId);
childNode->WaitTimeString = PhFormatString(L"%lu", WctNode->ThreadObject.WaitTime);
childNode->ContextSwitchesString = PhFormatString(L"%lu", WctNode->ThreadObject.ContextSwitches);
if (WctNode->LockObject.ObjectName[0] != L'\0')
childNode->ObjectNameString = PhFormatString(L"%s", WctNode->LockObject.ObjectName);
if (WctNode->LockObject.Timeout.QuadPart > 0)
{
SYSTEMTIME systemTime;
PPH_STRING dateString = NULL;
PPH_STRING timeString = NULL;
PhLargeIntegerToLocalSystemTime(&systemTime, &WctNode->LockObject.Timeout);
dateString = PhFormatDate(&systemTime, NULL);
timeString = PhFormatTime(&systemTime, NULL);
childNode->TimeoutString = PhFormatString(L"%s %s", dateString->Buffer, timeString->Buffer);
PhDereferenceObject(dateString);
PhDereferenceObject(timeString);
}
if (ParentNode)
{
childNode->HasChildren = FALSE;
// This is a child node.
childNode->Parent = ParentNode;
PhAddItemList(ParentNode->Children, childNode);
}
else
{
childNode->HasChildren = TRUE;
childNode->Node.Expanded = TRUE;
// This is a root node.
PhAddItemList(Context->NodeRootList, childNode);
}
}
PWCT_ROOT_NODE WeFindWindowNode(
_In_ PWCT_TREE_CONTEXT Context,
_In_ HWND WindowHandle
)
{
WCT_ROOT_NODE lookupWindowNode;
PWCT_ROOT_NODE lookupWindowNodePtr = &lookupWindowNode;
PWCT_ROOT_NODE *windowNode;
lookupWindowNode.Node.Index = HandleToUlong(WindowHandle);
windowNode = (PWCT_ROOT_NODE*)PhFindEntryHashtable(
Context->NodeHashtable,
&lookupWindowNodePtr
);
if (windowNode)
return *windowNode;
else
return NULL;
}
VOID WeRemoveWindowNode(
_In_ PWCT_TREE_CONTEXT Context,
_In_ PWCT_ROOT_NODE WindowNode
)
{
ULONG index = 0;
// 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);
}
BOOLEAN NTAPI WepWindowTreeNewCallback(
_In_ HWND hwnd,
_In_ PH_TREENEW_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2,
_In_opt_ PVOID Context
)
{
PWCT_TREE_CONTEXT context;
PWCT_ROOT_NODE node;
context = Context;
switch (Message)
{
case TreeNewGetChildren:
{
PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
node = (PWCT_ROOT_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)
{
getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items;
getChildren->NumberOfChildren = context->NodeList->Count;
}
}
}
return TRUE;
case TreeNewIsLeaf:
{
PPH_TREENEW_IS_LEAF isLeaf = (PPH_TREENEW_IS_LEAF)Parameter1;
node = (PWCT_ROOT_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 = (PPH_TREENEW_GET_CELL_TEXT)Parameter1;
node = (PWCT_ROOT_NODE)getCellText->Node;
switch (getCellText->Id)
{
case TREE_COLUMN_ITEM_TYPE:
{
switch (node->ObjectType)
{
case WctCriticalSectionType:
PhInitializeStringRef(&getCellText->Text, L"CriticalSection");
break;
case WctSendMessageType:
PhInitializeStringRef(&getCellText->Text, L"SendMessage");
break;
case WctMutexType:
PhInitializeStringRef(&getCellText->Text, L"Mutex");
break;
case WctAlpcType:
PhInitializeStringRef(&getCellText->Text, L"Alpc");
break;
case WctComType:
PhInitializeStringRef(&getCellText->Text, L"Com");
break;
case WctComActivationType:
PhInitializeStringRef(&getCellText->Text, L"ComActivation");
break;
case WctProcessWaitType:
PhInitializeStringRef(&getCellText->Text, L"ProcWait");
break;
case WctThreadType:
PhInitializeStringRef(&getCellText->Text, L"Thread");
break;
case WctThreadWaitType:
PhInitializeStringRef(&getCellText->Text, L"ThreadWait");
break;
case WctSocketIoType:
PhInitializeStringRef(&getCellText->Text, L"Socket I/O");
break;
case WctSmbIoType:
PhInitializeStringRef(&getCellText->Text, L"SMB I/O");
break;
case WctUnknownType:
case WctMaxType:
default:
PhInitializeStringRef(&getCellText->Text, L"Unknown");
break;
}
}
break;
case TREE_COLUMN_ITEM_STATUS:
{
switch (node->ObjectStatus)
{
case WctStatusNoAccess:
PhInitializeStringRef(&getCellText->Text, L"No Access");
break;
case WctStatusRunning:
PhInitializeStringRef(&getCellText->Text, L"Running");
break;
case WctStatusBlocked:
PhInitializeStringRef(&getCellText->Text, L"Blocked");
break;
case WctStatusPidOnly:
PhInitializeStringRef(&getCellText->Text, L"Pid Only");
break;
case WctStatusPidOnlyRpcss:
PhInitializeStringRef(&getCellText->Text, L"Pid Only (Rpcss)");
break;
case WctStatusOwned:
PhInitializeStringRef(&getCellText->Text, L"Owned");
break;
case WctStatusNotOwned:
PhInitializeStringRef(&getCellText->Text, L"Not Owned");
break;
case WctStatusAbandoned:
PhInitializeStringRef(&getCellText->Text, L"Abandoned");
break;
case WctStatusError:
PhInitializeStringRef(&getCellText->Text, L"Error");
break;
case WctStatusUnknown:
case WctStatusMax:
default:
PhInitializeStringRef(&getCellText->Text, L"Unknown");
break;
}
}
break;
case TREE_COLUMN_ITEM_NAME:
getCellText->Text = PhGetStringRef(node->ObjectNameString);
break;
case TREE_COLUMN_ITEM_TIMEOUT:
getCellText->Text = PhGetStringRef(node->TimeoutString);
break;
case TREE_COLUMN_ITEM_ALERTABLE:
{
if (node->Alertable)
{
PhInitializeStringRef(&getCellText->Text, L"true");
}
else
{
PhInitializeStringRef(&getCellText->Text, L"false");
}
}
break;
case TREE_COLUMN_ITEM_PROCESSID:
{
if (node->ObjectType == WctThreadType)
{
getCellText->Text = PhGetStringRef(node->ProcessIdString);
}
}
break;
case TREE_COLUMN_ITEM_THREADID:
{
if (node->ObjectType == WctThreadType)
{
getCellText->Text = PhGetStringRef(node->ThreadIdString);
}
}
break;
case TREE_COLUMN_ITEM_WAITTIME:
{
if (node->ObjectType == WctThreadType)
{
getCellText->Text = PhGetStringRef(node->WaitTimeString);
}
}
break;
case TREE_COLUMN_ITEM_CONTEXTSWITCH:
{
if (node->ObjectType == WctThreadType)
{
getCellText->Text = PhGetStringRef(node->ContextSwitchesString);
}
}
break;
default:
return FALSE;
}
getCellText->Flags = TN_CACHE;
}
return TRUE;
case TreeNewGetNodeColor:
{
PPH_TREENEW_GET_NODE_COLOR getNodeColor = (PPH_TREENEW_GET_NODE_COLOR)Parameter1;
node = (PWCT_ROOT_NODE)getNodeColor->Node;
if (node->IsDeadLocked)
{
getNodeColor->ForeColor = RGB(255, 0, 0);
}
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:
case TreeNewLeftDoubleClick:
case TreeNewNodeExpanding:
return TRUE;
case TreeNewContextMenu:
{
PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)Parameter1;
SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WCTSHOWCONTEXTMENU, MAKELONG(mouseEvent->Location.x, mouseEvent->Location.y));
}
return TRUE;
case TreeNewHeaderRightClick:
{
PH_TN_COLUMN_MENU_DATA data;
data.TreeNewHandle = hwnd;
data.MouseEvent = Parameter1;
data.DefaultSortColumn = 0;
data.DefaultSortOrder = AscendingSortOrder;
PhInitializeTreeNewColumnMenu(&data);
data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y);
PhHandleTreeNewColumnMenu(&data);
PhDeleteTreeNewColumnMenu(&data);
}
return TRUE;
}
return FALSE;
}
VOID WeClearWindowTree(
_In_ PWCT_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);
}
PWCT_ROOT_NODE WeGetSelectedWindowNode(
_In_ PWCT_TREE_CONTEXT Context
)
{
PWCT_ROOT_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_ PWCT_TREE_CONTEXT Context,
_Out_ PWCT_ROOT_NODE **Windows,
_Out_ PULONG NumberOfWindows
)
{
PPH_LIST list;
ULONG i;
list = PhCreateList(2);
for (i = 0; i < Context->NodeList->Count; i++)
{
PWCT_ROOT_NODE node = (PWCT_ROOT_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);
}

View File

@@ -0,0 +1,129 @@
/*
* Process Hacker Extra Plugins -
* Wait Chain Traversal (WCT) Plugin
*
* Copyright (C) 2014 dmex
*
* 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/>.
*/
#ifndef WNDTREE_H
#define WNDTREE_H
#define ID_WCTSHOWCONTEXTMENU 19000
#define PLUGIN_NAME L"dmex.WaitChainPlugin"
#define SETTING_NAME_TREE_LIST_COLUMNS (PLUGIN_NAME L".TreeListColumns")
#define SETTING_NAME_WINDOW_POSITION (PLUGIN_NAME L".WindowPosition")
#define SETTING_NAME_WINDOW_SIZE (PLUGIN_NAME L".WindowSize")
typedef enum _WCT_TREE_COLUMN_ITEM_NAME
{
TREE_COLUMN_ITEM_TYPE = 0,
TREE_COLUMN_ITEM_STATUS = 1,
TREE_COLUMN_ITEM_NAME = 2,
TREE_COLUMN_ITEM_TIMEOUT = 3,
TREE_COLUMN_ITEM_ALERTABLE = 4,
TREE_COLUMN_ITEM_PROCESSID = 5,
TREE_COLUMN_ITEM_THREADID = 6,
TREE_COLUMN_ITEM_WAITTIME = 7,
TREE_COLUMN_ITEM_CONTEXTSWITCH = 8,
TREE_COLUMN_ITEM_MAXIMUM
} WCT_TREE_COLUMN_ITEM_NAME;
typedef struct _WCT_ROOT_NODE
{
PH_TREENEW_NODE Node;
struct _WCT_ROOT_NODE* Parent;
PPH_LIST Children;
BOOLEAN HasChildren;
BOOLEAN Alertable;
WCT_OBJECT_TYPE ObjectType;
WCT_OBJECT_STATUS ObjectStatus;
PPH_STRING TimeoutString;
PPH_STRING ProcessIdString;
PPH_STRING ThreadIdString;
PPH_STRING WaitTimeString;
PPH_STRING ContextSwitchesString;
PPH_STRING ObjectNameString;
BOOLEAN IsDeadLocked;
HANDLE ProcessId;
HANDLE ThreadId;
PH_STRINGREF TextCache[TREE_COLUMN_ITEM_MAXIMUM];
WCHAR WindowHandleString[PH_PTR_STR_LEN_1];
} WCT_ROOT_NODE, *PWCT_ROOT_NODE;
typedef struct _WCT_TREE_CONTEXT
{
HWND ParentWindowHandle;
HWND TreeNewHandle;
ULONG TreeNewSortColumn;
PH_SORT_ORDER TreeNewSortOrder;
PPH_HASHTABLE NodeHashtable;
PPH_LIST NodeList;
PPH_LIST NodeRootList;
} WCT_TREE_CONTEXT, *PWCT_TREE_CONTEXT;
VOID WtcInitializeWindowTree(
_In_ HWND ParentWindowHandle,
_In_ HWND TreeNewHandle,
_Out_ PWCT_TREE_CONTEXT Context
);
VOID WtcDeleteWindowTree(
_In_ PWCT_TREE_CONTEXT Context
);
VOID WctAddChildWindowNode(
_In_ PWCT_TREE_CONTEXT Context,
_In_opt_ PWCT_ROOT_NODE ParentNode,
_In_ PWAITCHAIN_NODE_INFO WctNode,
_In_ BOOLEAN IsDeadLocked
);
PWCT_ROOT_NODE WeAddWindowNode(
_Inout_ PWCT_TREE_CONTEXT Context
);
PWCT_ROOT_NODE WeFindWindowNode(
_In_ PWCT_TREE_CONTEXT Context,
_In_ HWND WindowHandle
);
VOID WeRemoveWindowNode(
_In_ PWCT_TREE_CONTEXT Context,
_In_ PWCT_ROOT_NODE WindowNode
);
VOID WeClearWindowTree(
_In_ PWCT_TREE_CONTEXT Context
);
PWCT_ROOT_NODE WeGetSelectedWindowNode(
_In_ PWCT_TREE_CONTEXT Context
);
VOID WeGetSelectedWindowNodes(
_In_ PWCT_TREE_CONTEXT Context,
_Out_ PWCT_ROOT_NODE **Windows,
_Out_ PULONG NumberOfWindows
);
#endif