Files

3414 lines
123 KiB
C

/*
* Process Hacker -
* process tree list
*
* Copyright (C) 2010-2015 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/>.
*/
/*
* The process tree list manages a list of tree nodes and handles callback
* events generated by the underlying treenew control. Retrieval of
* certain types of process information is also performed here, on the
* GUI thread (see PH_PROCESS_NODE.ValidMask). This is done for columns
* that require data not supplied by the process provider.
*/
#include <phapp.h>
#include <settings.h>
#include <extmgri.h>
#include <phplug.h>
#include <cpysave.h>
#include <emenu.h>
#include <verify.h>
typedef enum _PHP_AGGREGATE_TYPE
{
AggregateTypeFloat,
AggregateTypeInt32,
AggregateTypeInt64,
AggregateTypeIntPtr
} PHP_AGGREGATE_TYPE;
typedef enum _PHP_AGGREGATE_LOCATION
{
AggregateLocationProcessNode,
AggregateLocationProcessItem
} PHP_AGGREGATE_LOCATION;
VOID PhpRemoveProcessNode(
_In_ PPH_PROCESS_NODE ProcessNode
);
VOID PhpUpdateNeedCyclesInformation(
VOID
);
VOID PhpUpdateProcessNodeCycles(
_Inout_ PPH_PROCESS_NODE ProcessNode
);
LONG PhpProcessTreeNewPostSortFunction(
_In_ LONG Result,
_In_ PVOID Node1,
_In_ PVOID Node2,
_In_ PH_SORT_ORDER SortOrder
);
BOOLEAN NTAPI PhpProcessTreeNewCallback(
_In_ HWND hwnd,
_In_ PH_TREENEW_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2,
_In_opt_ PVOID Context
);
static HWND ProcessTreeListHandle;
static ULONG ProcessTreeListSortColumn;
static PH_SORT_ORDER ProcessTreeListSortOrder;
static PH_CM_MANAGER ProcessTreeListCm;
static PPH_HASH_ENTRY ProcessNodeHashSet[256] = PH_HASH_SET_INIT; // hashtable of all nodes
static PPH_LIST ProcessNodeList; // list of all nodes, used when sorting is enabled
static PPH_LIST ProcessNodeRootList; // list of root nodes
BOOLEAN PhProcessTreeListStateHighlighting = TRUE;
static PPH_POINTER_LIST ProcessNodeStateList = NULL; // list of nodes which need to be processed
static PH_TN_FILTER_SUPPORT FilterSupport;
static BOOLEAN NeedCyclesInformation = FALSE;
static HDC GraphContext = NULL;
static ULONG GraphContextWidth = 0;
static ULONG GraphContextHeight = 0;
static HBITMAP GraphOldBitmap;
static HBITMAP GraphBitmap = NULL;
static PVOID GraphBits = NULL;
VOID PhProcessTreeListInitialization(
VOID
)
{
ProcessNodeList = PhCreateList(40);
ProcessNodeRootList = PhCreateList(10);
}
VOID PhInitializeProcessTreeList(
_In_ HWND hwnd
)
{
ProcessTreeListHandle = hwnd;
PhSetControlTheme(ProcessTreeListHandle, L"explorer");
TreeNew_SetExtendedFlags(hwnd, TN_FLAG_ITEM_DRAG_SELECT, TN_FLAG_ITEM_DRAG_SELECT);
SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT);
TreeNew_SetCallback(hwnd, PhpProcessTreeNewCallback, NULL);
TreeNew_SetMaxId(hwnd, PHPRTLC_MAXIMUM - 1);
TreeNew_SetRedraw(hwnd, FALSE);
// Default columns
PhAddTreeNewColumn(hwnd, PHPRTLC_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, -2, 0);
PhAddTreeNewColumn(hwnd, PHPRTLC_PID, TRUE, L"PID", 50, PH_ALIGN_RIGHT, 0, DT_RIGHT);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_CPU, TRUE, L"CPU", 45, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOTOTALRATE, TRUE, L"I/O Total Rate", 70, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEBYTES, TRUE, L"Private Bytes", 70, PH_ALIGN_RIGHT, 3, DT_RIGHT, TRUE);
PhAddTreeNewColumn(hwnd, PHPRTLC_USERNAME, TRUE, L"User Name", 140, PH_ALIGN_LEFT, 4, DT_PATH_ELLIPSIS);
PhAddTreeNewColumn(hwnd, PHPRTLC_DESCRIPTION, TRUE, L"Description", 180, PH_ALIGN_LEFT, 5, 0);
// Customizable columns (1)
PhAddTreeNewColumn(hwnd, PHPRTLC_COMPANYNAME, FALSE, L"Company Name", 180, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumn(hwnd, PHPRTLC_VERSION, FALSE, L"Version", 100, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumn(hwnd, PHPRTLC_FILENAME, FALSE, L"File Name", 180, PH_ALIGN_LEFT, -1, DT_PATH_ELLIPSIS);
PhAddTreeNewColumn(hwnd, PHPRTLC_COMMANDLINE, FALSE, L"Command Line", 180, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKPRIVATEBYTES, FALSE, L"Peak Private Bytes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_WORKINGSET, FALSE, L"Working Set", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKWORKINGSET, FALSE, L"Peak Working Set", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEWS, FALSE, L"Private WS", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_SHAREDWS, FALSE, L"Shared WS", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_SHAREABLEWS, FALSE, L"Shareable WS", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_VIRTUALSIZE, FALSE, L"Virtual Size", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKVIRTUALSIZE, FALSE, L"Peak Virtual Size", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEFAULTS, FALSE, L"Page Faults", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumn(hwnd, PHPRTLC_SESSIONID, FALSE, L"Session ID", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIORITYCLASS, FALSE, L"Priority Class", 100, PH_ALIGN_LEFT, -1, 0, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_BASEPRIORITY, FALSE, L"Base Priority", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
// Customizable columns (2)
PhAddTreeNewColumnEx(hwnd, PHPRTLC_THREADS, FALSE, L"Threads", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_HANDLES, FALSE, L"Handles", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_GDIHANDLES, FALSE, L"GDI Handles", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_USERHANDLES, FALSE, L"USER Handles", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IORORATE, FALSE, L"I/O Read+Other Rate", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRATE, FALSE, L"I/O Write Rate", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumn(hwnd, PHPRTLC_INTEGRITY, FALSE, L"Integrity", 100, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOPRIORITY, FALSE, L"I/O Priority", 70, PH_ALIGN_LEFT, -1, 0, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEPRIORITY, FALSE, L"Page Priority", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_STARTTIME, FALSE, L"Start Time", 100, PH_ALIGN_LEFT, -1, 0, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_TOTALCPUTIME, FALSE, L"Total CPU Time", 90, PH_ALIGN_LEFT, -1, 0, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_KERNELCPUTIME, FALSE, L"Kernel CPU Time", 90, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_USERCPUTIME, FALSE, L"User CPU Time", 90, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumn(hwnd, PHPRTLC_VERIFICATIONSTATUS, FALSE, L"Verification Status", 70, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumn(hwnd, PHPRTLC_VERIFIEDSIGNER, FALSE, L"Verified Signer", 100, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_ASLR, FALSE, L"ASLR", 50, PH_ALIGN_LEFT, -1, 0, TRUE);
PhAddTreeNewColumn(hwnd, PHPRTLC_RELATIVESTARTTIME, FALSE, L"Relative Start Time", 180, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumn(hwnd, PHPRTLC_BITS, FALSE, L"Bits", 50, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_ELEVATION, FALSE, L"Elevation", 60, PH_ALIGN_LEFT, -1, 0, TRUE);
PhAddTreeNewColumn(hwnd, PHPRTLC_WINDOWTITLE, FALSE, L"Window Title", 120, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_WINDOWSTATUS, FALSE, L"Window Status", 60, PH_ALIGN_LEFT, -1, 0, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_CYCLES, FALSE, L"Cycles", 110, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_CYCLESDELTA, FALSE, L"Cycles Delta", 90, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx2(hwnd, PHPRTLC_CPUHISTORY, FALSE, L"CPU History", 100, PH_ALIGN_LEFT, -1, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING);
PhAddTreeNewColumnEx2(hwnd, PHPRTLC_PRIVATEBYTESHISTORY, FALSE, L"Private Bytes History", 100, PH_ALIGN_LEFT, -1, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING);
PhAddTreeNewColumnEx2(hwnd, PHPRTLC_IOHISTORY, FALSE, L"I/O History", 100, PH_ALIGN_LEFT, -1, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING);
PhAddTreeNewColumn(hwnd, PHPRTLC_DEPSTATUS, FALSE, L"DEP Status", 100, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_VIRTUALIZED, FALSE, L"Virtualized", 80, PH_ALIGN_LEFT, -1, 0, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_CONTEXTSWITCHES, FALSE, L"Context Switches", 100, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_CONTEXTSWITCHESDELTA, FALSE, L"Context Switches Delta", 80, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEFAULTSDELTA, FALSE, L"Page Faults Delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
// I/O group columns
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADS, FALSE, L"I/O Reads", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITES, FALSE, L"I/O Writes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHER, FALSE, L"I/O Other", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADBYTES, FALSE, L"I/O Read Bytes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITEBYTES, FALSE, L"I/O Write Bytes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHERBYTES, FALSE, L"I/O Other Bytes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADSDELTA, FALSE, L"I/O Reads Delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITESDELTA, FALSE, L"I/O Writes Delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHERDELTA, FALSE, L"I/O Other Delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
// Customizable columns (3)
PhAddTreeNewColumn(hwnd, PHPRTLC_OSCONTEXT, FALSE, L"OS Context", 100, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEDPOOL, FALSE, L"Paged Pool", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKPAGEDPOOL, FALSE, L"Peak Paged Pool", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_NONPAGEDPOOL, FALSE, L"Non-Paged Pool", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKNONPAGEDPOOL, FALSE, L"Peak Non-Paged Pool", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_MINIMUMWORKINGSET, FALSE, L"Minimum Working Set", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_MAXIMUMWORKINGSET, FALSE, L"Maximum Working Set", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEBYTESDELTA, FALSE, L"Private Bytes Delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE);
PhAddTreeNewColumn(hwnd, PHPRTLC_SUBSYSTEM, FALSE, L"Subsystem", 110, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumn(hwnd, PHPRTLC_PACKAGENAME, FALSE, L"Package Name", 160, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumn(hwnd, PHPRTLC_APPID, FALSE, L"App ID", 160, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumn(hwnd, PHPRTLC_DPIAWARENESS, FALSE, L"DPI Awareness", 110, PH_ALIGN_LEFT, -1, 0);
PhAddTreeNewColumnEx(hwnd, PHPRTLC_CFGUARD, FALSE, L"CF Guard", 70, PH_ALIGN_LEFT, -1, 0, TRUE);
TreeNew_SetRedraw(hwnd, TRUE);
TreeNew_SetTriState(hwnd, TRUE);
TreeNew_SetSort(hwnd, 0, NoSortOrder);
PhCmInitializeManager(&ProcessTreeListCm, hwnd, PHPRTLC_MAXIMUM, PhpProcessTreeNewPostSortFunction);
if (PhPluginsEnabled)
{
PH_PLUGIN_TREENEW_INFORMATION treeNewInfo;
treeNewInfo.TreeNewHandle = hwnd;
treeNewInfo.CmData = &ProcessTreeListCm;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), &treeNewInfo);
}
PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, ProcessNodeList);
}
VOID PhLoadSettingsProcessTreeList(
VOID
)
{
PPH_STRING settings;
PPH_STRING sortSettings;
settings = PhGetStringSetting(L"ProcessTreeListColumns");
sortSettings = PhGetStringSetting(L"ProcessTreeListSort");
PhCmLoadSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &settings->sr, &sortSettings->sr);
PhDereferenceObject(settings);
PhDereferenceObject(sortSettings);
if (PhGetIntegerSetting(L"EnableInstantTooltips"))
{
SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, 0);
}
PhpUpdateNeedCyclesInformation();
}
VOID PhSaveSettingsProcessTreeList(
VOID
)
{
PPH_STRING settings;
PPH_STRING sortSettings;
settings = PhCmSaveSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &sortSettings);
PhSetStringSetting2(L"ProcessTreeListColumns", &settings->sr);
PhSetStringSetting2(L"ProcessTreeListSort", &sortSettings->sr);
PhDereferenceObject(settings);
PhDereferenceObject(sortSettings);
}
VOID PhReloadSettingsProcessTreeList(
VOID
)
{
SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL,
PhGetIntegerSetting(L"EnableInstantTooltips") ? 0 : -1);
}
struct _PH_TN_FILTER_SUPPORT *PhGetFilterSupportProcessTreeList(
VOID
)
{
return &FilterSupport;
}
FORCEINLINE BOOLEAN PhCompareProcessNode(
_In_ PPH_PROCESS_NODE Value1,
_In_ PPH_PROCESS_NODE Value2
)
{
return Value1->ProcessId == Value2->ProcessId;
}
FORCEINLINE ULONG PhHashProcessNode(
_In_ PPH_PROCESS_NODE Value
)
{
return HandleToUlong(Value->ProcessId) / 4;
}
FORCEINLINE BOOLEAN PhpValidateParentCreateTime(
_In_ PPH_PROCESS_NODE Child,
_In_ PPH_PROCESS_NODE Parent
)
{
return
PH_IS_FAKE_PROCESS_ID(Child->ProcessId) ||
Parent->ProcessItem->CreateTime.QuadPart <= Child->ProcessItem->CreateTime.QuadPart;
}
PPH_PROCESS_NODE PhAddProcessNode(
_In_ PPH_PROCESS_ITEM ProcessItem,
_In_ ULONG RunId
)
{
PPH_PROCESS_NODE processNode;
PPH_PROCESS_NODE parentNode;
ULONG i;
processNode = PhAllocate(PhEmGetObjectSize(EmProcessNodeType, sizeof(PH_PROCESS_NODE)));
memset(processNode, 0, sizeof(PH_PROCESS_NODE));
PhInitializeTreeNewNode(&processNode->Node);
if (PhProcessTreeListStateHighlighting && RunId != 1)
{
PhChangeShStateTn(
&processNode->Node,
&processNode->ShState,
&ProcessNodeStateList,
NewItemState,
PhCsColorNew,
NULL
);
}
processNode->ProcessId = ProcessItem->ProcessId;
processNode->ProcessItem = ProcessItem;
PhReferenceObject(ProcessItem);
memset(processNode->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM);
processNode->Node.TextCache = processNode->TextCache;
processNode->Node.TextCacheSize = PHPRTLC_MAXIMUM;
processNode->Children = PhCreateList(1);
// Find this process' parent and add the process to it if we found it.
if (
(parentNode = PhFindProcessNode(ProcessItem->ParentProcessId)) &&
PhpValidateParentCreateTime(processNode, parentNode)
)
{
PhAddItemList(parentNode->Children, processNode);
processNode->Parent = parentNode;
}
else
{
// No parent, add to root list.
processNode->Parent = NULL;
PhAddItemList(ProcessNodeRootList, processNode);
}
// Find this process' children and move them to this node.
for (i = 0; i < ProcessNodeRootList->Count; i++)
{
PPH_PROCESS_NODE node = ProcessNodeRootList->Items[i];
if (
node != processNode && // for cases where the parent PID = PID (e.g. System Idle Process)
node->ProcessItem->ParentProcessId == ProcessItem->ProcessId &&
PhpValidateParentCreateTime(node, processNode)
)
{
node->Parent = processNode;
PhAddItemList(processNode->Children, node);
}
}
for (i = 0; i < processNode->Children->Count; i++)
{
PhRemoveItemList(
ProcessNodeRootList,
PhFindItemList(ProcessNodeRootList, processNode->Children->Items[i])
);
}
PhAddEntryHashSet(
ProcessNodeHashSet,
PH_HASH_SET_SIZE(ProcessNodeHashSet),
&processNode->HashEntry,
PhHashProcessNode(processNode)
);
PhAddItemList(ProcessNodeList, processNode);
if (PhCsCollapseServicesOnStart)
{
static PH_STRINGREF servicesBaseName = PH_STRINGREF_INIT(L"\\services.exe");
static BOOLEAN servicesFound = FALSE;
static PPH_STRING servicesFileName = NULL;
if (!servicesFound)
{
if (!servicesFileName)
{
PPH_STRING systemDirectory;
systemDirectory = PhGetSystemDirectory();
servicesFileName = PhConcatStringRef2(&systemDirectory->sr, &servicesBaseName);
PhDereferenceObject(systemDirectory);
}
// If this process is services.exe, collapse the node and free the string.
if (
ProcessItem->FileName &&
PhEqualString(ProcessItem->FileName, servicesFileName, TRUE)
)
{
processNode->Node.Expanded = FALSE;
PhDereferenceObject(servicesFileName);
servicesFileName = NULL;
servicesFound = TRUE;
}
}
}
if (WindowsVersion >= WINDOWS_7 && PhEnableCycleCpuUsage && ProcessItem->ProcessId == INTERRUPTS_PROCESS_ID)
PhInitializeStringRef(&processNode->DescriptionText, L"Interrupts and DPCs");
if (FilterSupport.FilterList)
processNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &processNode->Node);
PhEmCallObjectOperation(EmProcessNodeType, processNode, EmObjectCreate);
TreeNew_NodesStructured(ProcessTreeListHandle);
return processNode;
}
PPH_PROCESS_NODE PhFindProcessNode(
_In_ HANDLE ProcessId
)
{
PH_PROCESS_NODE lookupNode;
PPH_HASH_ENTRY entry;
PPH_PROCESS_NODE node;
lookupNode.ProcessId = ProcessId;
entry = PhFindEntryHashSet(
ProcessNodeHashSet,
PH_HASH_SET_SIZE(ProcessNodeHashSet),
PhHashProcessNode(&lookupNode)
);
for (; entry; entry = entry->Next)
{
node = CONTAINING_RECORD(entry, PH_PROCESS_NODE, HashEntry);
if (PhCompareProcessNode(&lookupNode, node))
return node;
}
return NULL;
}
VOID PhRemoveProcessNode(
_In_ PPH_PROCESS_NODE ProcessNode
)
{
// Remove from the hashtable here to avoid problems in case the key is re-used.
PhRemoveEntryHashSet(ProcessNodeHashSet, PH_HASH_SET_SIZE(ProcessNodeHashSet), &ProcessNode->HashEntry);
if (PhProcessTreeListStateHighlighting)
{
PhChangeShStateTn(
&ProcessNode->Node,
&ProcessNode->ShState,
&ProcessNodeStateList,
RemovingItemState,
PhCsColorRemoved,
ProcessTreeListHandle
);
}
else
{
PhpRemoveProcessNode(ProcessNode);
}
}
VOID PhpRemoveProcessNode(
_In_ PPH_PROCESS_NODE ProcessNode
)
{
ULONG index;
ULONG i;
PhEmCallObjectOperation(EmProcessNodeType, ProcessNode, EmObjectDelete);
if (ProcessNode->Parent)
{
// Remove the node from its parent.
if ((index = PhFindItemList(ProcessNode->Parent->Children, ProcessNode)) != -1)
PhRemoveItemList(ProcessNode->Parent->Children, index);
}
else
{
// Remove the node from the root list.
if ((index = PhFindItemList(ProcessNodeRootList, ProcessNode)) != -1)
PhRemoveItemList(ProcessNodeRootList, index);
}
// Move the node's children to the root list.
for (i = 0; i < ProcessNode->Children->Count; i++)
{
PPH_PROCESS_NODE node = ProcessNode->Children->Items[i];
node->Parent = NULL;
PhAddItemList(ProcessNodeRootList, node);
}
// Remove from list and cleanup.
if ((index = PhFindItemList(ProcessNodeList, ProcessNode)) != -1)
PhRemoveItemList(ProcessNodeList, index);
PhDereferenceObject(ProcessNode->Children);
if (ProcessNode->WindowText) PhDereferenceObject(ProcessNode->WindowText);
if (ProcessNode->AppIdText) PhDereferenceObject(ProcessNode->AppIdText);
if (ProcessNode->TooltipText) PhDereferenceObject(ProcessNode->TooltipText);
if (ProcessNode->IoTotalRateText) PhDereferenceObject(ProcessNode->IoTotalRateText);
if (ProcessNode->PrivateBytesText) PhDereferenceObject(ProcessNode->PrivateBytesText);
if (ProcessNode->PeakPrivateBytesText) PhDereferenceObject(ProcessNode->PeakPrivateBytesText);
if (ProcessNode->WorkingSetText) PhDereferenceObject(ProcessNode->WorkingSetText);
if (ProcessNode->PeakWorkingSetText) PhDereferenceObject(ProcessNode->PeakWorkingSetText);
if (ProcessNode->PrivateWsText) PhDereferenceObject(ProcessNode->PrivateWsText);
if (ProcessNode->SharedWsText) PhDereferenceObject(ProcessNode->SharedWsText);
if (ProcessNode->ShareableWsText) PhDereferenceObject(ProcessNode->ShareableWsText);
if (ProcessNode->VirtualSizeText) PhDereferenceObject(ProcessNode->VirtualSizeText);
if (ProcessNode->PeakVirtualSizeText) PhDereferenceObject(ProcessNode->PeakVirtualSizeText);
if (ProcessNode->PageFaultsText) PhDereferenceObject(ProcessNode->PageFaultsText);
if (ProcessNode->IoRoRateText) PhDereferenceObject(ProcessNode->IoRoRateText);
if (ProcessNode->IoWRateText) PhDereferenceObject(ProcessNode->IoWRateText);
if (ProcessNode->StartTimeText) PhDereferenceObject(ProcessNode->StartTimeText);
if (ProcessNode->RelativeStartTimeText) PhDereferenceObject(ProcessNode->RelativeStartTimeText);
if (ProcessNode->WindowTitleText) PhDereferenceObject(ProcessNode->WindowTitleText);
if (ProcessNode->CyclesText) PhDereferenceObject(ProcessNode->CyclesText);
if (ProcessNode->CyclesDeltaText) PhDereferenceObject(ProcessNode->CyclesDeltaText);
if (ProcessNode->ContextSwitchesText) PhDereferenceObject(ProcessNode->ContextSwitchesText);
if (ProcessNode->ContextSwitchesDeltaText) PhDereferenceObject(ProcessNode->ContextSwitchesDeltaText);
if (ProcessNode->PageFaultsDeltaText) PhDereferenceObject(ProcessNode->PageFaultsDeltaText);
for (i = 0; i < PHPRTLC_IOGROUP_COUNT; i++)
if (ProcessNode->IoGroupText[i]) PhDereferenceObject(ProcessNode->IoGroupText[i]);
if (ProcessNode->PagedPoolText) PhDereferenceObject(ProcessNode->PagedPoolText);
if (ProcessNode->PeakPagedPoolText) PhDereferenceObject(ProcessNode->PeakPagedPoolText);
if (ProcessNode->NonPagedPoolText) PhDereferenceObject(ProcessNode->NonPagedPoolText);
if (ProcessNode->PeakNonPagedPoolText) PhDereferenceObject(ProcessNode->PeakNonPagedPoolText);
if (ProcessNode->MinimumWorkingSetText) PhDereferenceObject(ProcessNode->MinimumWorkingSetText);
if (ProcessNode->MaximumWorkingSetText) PhDereferenceObject(ProcessNode->MaximumWorkingSetText);
if (ProcessNode->PrivateBytesDeltaText) PhDereferenceObject(ProcessNode->PrivateBytesDeltaText);
PhDeleteGraphBuffers(&ProcessNode->CpuGraphBuffers);
PhDeleteGraphBuffers(&ProcessNode->PrivateGraphBuffers);
PhDeleteGraphBuffers(&ProcessNode->IoGraphBuffers);
PhDereferenceObject(ProcessNode->ProcessItem);
PhFree(ProcessNode);
TreeNew_NodesStructured(ProcessTreeListHandle);
}
VOID PhUpdateProcessNode(
_In_ PPH_PROCESS_NODE ProcessNode
)
{
memset(ProcessNode->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM);
if (ProcessNode->TooltipText)
{
PhDereferenceObject(ProcessNode->TooltipText);
ProcessNode->TooltipText = NULL;
}
PhInvalidateTreeNewNode(&ProcessNode->Node, TN_CACHE_COLOR | TN_CACHE_ICON);
TreeNew_InvalidateNode(ProcessTreeListHandle, &ProcessNode->Node);
}
VOID PhTickProcessNodes(
VOID
)
{
ULONG i;
PH_TREENEW_VIEW_PARTS viewParts;
BOOLEAN fullyInvalidated;
RECT rect;
// Text invalidation, node updates
for (i = 0; i < ProcessNodeList->Count; i++)
{
PPH_PROCESS_NODE node = ProcessNodeList->Items[i];
// The name and PID never change, so we don't invalidate that.
memset(&node->TextCache[2], 0, sizeof(PH_STRINGREF) * (PHPRTLC_MAXIMUM - 2));
node->ValidMask &= PHPN_OSCONTEXT | PHPN_IMAGE | PHPN_DPIAWARENESS; // Items that always remain valid
// Invalidate graph buffers.
node->CpuGraphBuffers.Valid = FALSE;
node->PrivateGraphBuffers.Valid = FALSE;
node->IoGraphBuffers.Valid = FALSE;
// Updates cycles if necessary.
if (NeedCyclesInformation)
PhpUpdateProcessNodeCycles(node);
}
fullyInvalidated = FALSE;
if (ProcessTreeListSortOrder != NoSortOrder)
{
// Force a rebuild to sort the items.
TreeNew_NodesStructured(ProcessTreeListHandle);
fullyInvalidated = TRUE;
}
// State highlighting
PH_TICK_SH_STATE_TN(PH_PROCESS_NODE, ShState, ProcessNodeStateList, PhpRemoveProcessNode, PhCsHighlightingDuration, ProcessTreeListHandle, TRUE, &fullyInvalidated);
if (!fullyInvalidated)
{
// The first column doesn't need to be invalidated because the process name never changes, and
// icon changes are handled by the modified event. This small optimization can save more than
// 10 million cycles per update (on my machine).
TreeNew_GetViewParts(ProcessTreeListHandle, &viewParts);
rect.left = viewParts.NormalLeft;
rect.top = viewParts.HeaderHeight;
rect.right = viewParts.ClientRect.right - viewParts.VScrollWidth;
rect.bottom = viewParts.ClientRect.bottom;
InvalidateRect(ProcessTreeListHandle, &rect, FALSE);
}
}
static VOID PhpNeedGraphContext(
_In_ HDC hdc,
_In_ ULONG Width,
_In_ ULONG Height
)
{
BITMAPINFOHEADER header;
// If we already have a graph context and it's the right size, then return immediately.
if (GraphContextWidth == Width && GraphContextHeight == Height)
return;
if (GraphContext)
{
// The original bitmap must be selected back into the context, otherwise
// the bitmap can't be deleted.
SelectObject(GraphContext, GraphBitmap);
DeleteObject(GraphBitmap);
DeleteDC(GraphContext);
GraphContext = NULL;
GraphBitmap = NULL;
GraphBits = NULL;
}
GraphContext = CreateCompatibleDC(hdc);
memset(&header, 0, sizeof(BITMAPINFOHEADER));
header.biSize = sizeof(BITMAPINFOHEADER);
header.biWidth = Width;
header.biHeight = Height;
header.biPlanes = 1;
header.biBitCount = 32;
GraphBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &GraphBits, NULL, 0);
GraphOldBitmap = SelectObject(GraphContext, GraphBitmap);
}
static BOOLEAN PhpFormatInt32GroupDigits(
_In_ ULONG Value,
_Out_writes_bytes_(BufferLength) PWCHAR Buffer,
_In_ ULONG BufferLength,
_Out_opt_ PPH_STRINGREF String
)
{
PH_FORMAT format;
SIZE_T returnLength;
PhInitFormatU(&format, Value);
format.Type |= FormatGroupDigits;
if (PhFormatToBuffer(&format, 1, Buffer, BufferLength, &returnLength))
{
if (String)
{
String->Buffer = Buffer;
String->Length = returnLength - sizeof(WCHAR);
}
return TRUE;
}
else
{
return FALSE;
}
}
FORCEINLINE PVOID PhpFieldForAggregate(
_In_ PPH_PROCESS_NODE ProcessNode,
_In_ PHP_AGGREGATE_LOCATION Location,
_In_ SIZE_T FieldOffset
)
{
PVOID object;
switch (Location)
{
case AggregateLocationProcessNode:
object = ProcessNode;
break;
case AggregateLocationProcessItem:
object = ProcessNode->ProcessItem;
break;
default:
PhRaiseStatus(STATUS_INVALID_PARAMETER);
}
return PTR_ADD_OFFSET(object, FieldOffset);
}
FORCEINLINE VOID PhpAccumulateField(
_Inout_ PVOID Accumulator,
_In_ PVOID Value,
_In_ PHP_AGGREGATE_TYPE Type
)
{
switch (Type)
{
case AggregateTypeFloat:
*(PFLOAT)Accumulator += *(PFLOAT)Value;
break;
case AggregateTypeInt32:
*(PULONG)Accumulator += *(PULONG)Value;
break;
case AggregateTypeInt64:
*(PULONG64)Accumulator += *(PULONG64)Value;
break;
case AggregateTypeIntPtr:
*(PULONG_PTR)Accumulator += *(PULONG_PTR)Value;
break;
}
}
static VOID PhpAggregateField(
_In_ PPH_PROCESS_NODE ProcessNode,
_In_ PHP_AGGREGATE_TYPE Type,
_In_ PHP_AGGREGATE_LOCATION Location,
_In_ SIZE_T FieldOffset,
_Inout_ PVOID AggregatedValue
)
{
ULONG i;
PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type);
for (i = 0; i < ProcessNode->Children->Count; i++)
{
PhpAggregateField(ProcessNode->Children->Items[i], Type, Location, FieldOffset, AggregatedValue);
}
}
static VOID PhpAggregateFieldIfNeeded(
_In_ PPH_PROCESS_NODE ProcessNode,
_In_ PHP_AGGREGATE_TYPE Type,
_In_ PHP_AGGREGATE_LOCATION Location,
_In_ SIZE_T FieldOffset,
_Inout_ PVOID AggregatedValue
)
{
if (!PhCsPropagateCpuUsage || ProcessNode->Node.Expanded || ProcessTreeListSortOrder != NoSortOrder)
{
PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type);
}
else
{
PhpAggregateField(ProcessNode, Type, Location, FieldOffset, AggregatedValue);
}
}
static VOID PhpUpdateProcessNodeWsCounters(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_WSCOUNTERS))
{
BOOLEAN success = FALSE;
HANDLE processHandle;
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION,
ProcessNode->ProcessItem->ProcessId
)))
{
if (NT_SUCCESS(PhGetProcessWsCounters(
processHandle,
&ProcessNode->WsCounters
)))
success = TRUE;
NtClose(processHandle);
}
if (!success)
memset(&ProcessNode->WsCounters, 0, sizeof(PH_PROCESS_WS_COUNTERS));
ProcessNode->ValidMask |= PHPN_WSCOUNTERS;
}
}
static VOID PhpUpdateProcessNodeGdiUserHandles(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_GDIUSERHANDLES))
{
if (ProcessNode->ProcessItem->QueryHandle)
{
ProcessNode->GdiHandles = GetGuiResources(ProcessNode->ProcessItem->QueryHandle, GR_GDIOBJECTS);
ProcessNode->UserHandles = GetGuiResources(ProcessNode->ProcessItem->QueryHandle, GR_USEROBJECTS);
}
else
{
ProcessNode->GdiHandles = 0;
ProcessNode->UserHandles = 0;
}
ProcessNode->ValidMask |= PHPN_GDIUSERHANDLES;
}
}
static VOID PhpUpdateProcessNodeIoPagePriority(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_IOPAGEPRIORITY))
{
if (ProcessNode->ProcessItem->QueryHandle)
{
if (!NT_SUCCESS(PhGetProcessIoPriority(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->IoPriority)))
ProcessNode->IoPriority = -1;
if (!NT_SUCCESS(PhGetProcessPagePriority(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->PagePriority)))
ProcessNode->PagePriority = -1;
}
else
{
ProcessNode->IoPriority = -1;
ProcessNode->PagePriority = -1;
}
ProcessNode->ValidMask |= PHPN_IOPAGEPRIORITY;
}
}
static VOID PhpUpdateProcessNodeWindow(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_WINDOW))
{
ProcessNode->WindowHandle = PhGetProcessMainWindow(ProcessNode->ProcessId, ProcessNode->ProcessItem->QueryHandle);
PhClearReference(&ProcessNode->WindowText);
if (ProcessNode->WindowHandle)
{
PhGetWindowTextEx(ProcessNode->WindowHandle, PH_GET_WINDOW_TEXT_INTERNAL, &ProcessNode->WindowText);
ProcessNode->WindowHung = !!IsHungAppWindow(ProcessNode->WindowHandle);
}
ProcessNode->ValidMask |= PHPN_WINDOW;
}
}
static VOID PhpUpdateProcessNodeDepStatus(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_DEPSTATUS))
{
HANDLE processHandle;
ULONG depStatus;
depStatus = 0;
#ifdef _WIN64
if (ProcessNode->ProcessItem->IsWow64)
#else
if (TRUE)
#endif
{
if (NT_SUCCESS(PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION,
ProcessNode->ProcessItem->ProcessId
)))
{
PhGetProcessDepStatus(processHandle, &depStatus);
NtClose(processHandle);
}
}
else
{
if (ProcessNode->ProcessItem->QueryHandle)
depStatus = PH_PROCESS_DEP_ENABLED | PH_PROCESS_DEP_PERMANENT;
}
ProcessNode->DepStatus = depStatus;
ProcessNode->ValidMask |= PHPN_DEPSTATUS;
}
}
static VOID PhpUpdateProcessNodeToken(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_TOKEN))
{
HANDLE tokenHandle;
ProcessNode->VirtualizationAllowed = FALSE;
ProcessNode->VirtualizationEnabled = FALSE;
if (WINDOWS_HAS_UAC && ProcessNode->ProcessItem->QueryHandle)
{
if (NT_SUCCESS(PhOpenProcessToken(
&tokenHandle,
TOKEN_QUERY,
ProcessNode->ProcessItem->QueryHandle
)))
{
if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &ProcessNode->VirtualizationAllowed)) &&
ProcessNode->VirtualizationAllowed)
{
if (!NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &ProcessNode->VirtualizationEnabled)))
{
ProcessNode->VirtualizationAllowed = FALSE; // display N/A on error
}
}
NtClose(tokenHandle);
}
}
ProcessNode->ValidMask |= PHPN_TOKEN;
}
}
static VOID PhpUpdateProcessOsContext(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_OSCONTEXT))
{
HANDLE processHandle;
if (WindowsVersion >= WINDOWS_7)
{
if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId)))
{
if (NT_SUCCESS(PhGetProcessSwitchContext(processHandle, &ProcessNode->OsContextGuid)))
{
if (memcmp(&ProcessNode->OsContextGuid, &WINTHRESHOLD_CONTEXT_GUID, sizeof(GUID)) == 0)
ProcessNode->OsContextVersion = WINDOWS_10;
else if (memcmp(&ProcessNode->OsContextGuid, &WINBLUE_CONTEXT_GUID, sizeof(GUID)) == 0)
ProcessNode->OsContextVersion = WINDOWS_8_1;
else if (memcmp(&ProcessNode->OsContextGuid, &WIN8_CONTEXT_GUID, sizeof(GUID)) == 0)
ProcessNode->OsContextVersion = WINDOWS_8;
else if (memcmp(&ProcessNode->OsContextGuid, &WIN7_CONTEXT_GUID, sizeof(GUID)) == 0)
ProcessNode->OsContextVersion = WINDOWS_7;
else if (memcmp(&ProcessNode->OsContextGuid, &VISTA_CONTEXT_GUID, sizeof(GUID)) == 0)
ProcessNode->OsContextVersion = WINDOWS_VISTA;
else if (memcmp(&ProcessNode->OsContextGuid, &XP_CONTEXT_GUID, sizeof(GUID)) == 0)
ProcessNode->OsContextVersion = WINDOWS_XP;
}
NtClose(processHandle);
}
}
ProcessNode->ValidMask |= PHPN_OSCONTEXT;
}
}
static VOID PhpUpdateProcessNodeQuotaLimits(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_QUOTALIMITS))
{
QUOTA_LIMITS quotaLimits;
if (ProcessNode->ProcessItem->QueryHandle && NT_SUCCESS(NtQueryInformationProcess(
ProcessNode->ProcessItem->QueryHandle,
ProcessQuotaLimits,
&quotaLimits,
sizeof(QUOTA_LIMITS),
NULL
)))
{
ProcessNode->MinimumWorkingSetSize = quotaLimits.MinimumWorkingSetSize;
ProcessNode->MaximumWorkingSetSize = quotaLimits.MaximumWorkingSetSize;
}
else
{
ProcessNode->MinimumWorkingSetSize = 0;
ProcessNode->MaximumWorkingSetSize = 0;
}
ProcessNode->ValidMask |= PHPN_QUOTALIMITS;
}
}
static VOID PhpUpdateProcessNodeImage(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_IMAGE))
{
HANDLE processHandle;
PROCESS_BASIC_INFORMATION basicInfo;
PVOID imageBaseAddress;
PH_REMOTE_MAPPED_IMAGE mappedImage;
if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId)))
{
if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo)))
{
if (NT_SUCCESS(PhReadVirtualMemory(
processHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ImageBaseAddress)),
&imageBaseAddress,
sizeof(PVOID),
NULL
)))
{
if (NT_SUCCESS(PhLoadRemoteMappedImage(processHandle, imageBaseAddress, &mappedImage)))
{
ProcessNode->ImageCharacteristics = mappedImage.NtHeaders->FileHeader.Characteristics;
if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
ProcessNode->ImageSubsystem = ((PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader)->Subsystem;
ProcessNode->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader)->DllCharacteristics;
}
else if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
ProcessNode->ImageSubsystem = ((PIMAGE_OPTIONAL_HEADER64)&mappedImage.NtHeaders->OptionalHeader)->Subsystem;
ProcessNode->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER64)&mappedImage.NtHeaders->OptionalHeader)->DllCharacteristics;
}
PhUnloadRemoteMappedImage(&mappedImage);
}
}
}
NtClose(processHandle);
}
ProcessNode->ValidMask |= PHPN_IMAGE;
}
}
static VOID PhpUpdateProcessNodeAppId(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (!(ProcessNode->ValidMask & PHPN_APPID))
{
HANDLE processHandle;
ULONG windowFlags;
PPH_STRING windowTitle;
PhClearReference(&ProcessNode->AppIdText);
if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId)))
{
if (WindowsVersion >= WINDOWS_7)
{
if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessNode->ProcessId)))
goto Done;
}
else
{
goto Done;
}
}
if (NT_SUCCESS(PhGetProcessWindowTitle(
processHandle,
&windowFlags,
&windowTitle
)))
{
if (windowFlags & STARTF_TITLEISAPPID)
ProcessNode->AppIdText = windowTitle;
else
PhDereferenceObject(windowTitle);
}
NtClose(processHandle);
Done:
ProcessNode->ValidMask |= PHPN_APPID;
}
}
static VOID PhpUpdateProcessNodeDpiAwareness(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static BOOL (WINAPI *getProcessDpiAwarenessInternal)(
_In_ HANDLE hprocess,
_Out_ ULONG *value
);
if (PhBeginInitOnce(&initOnce))
{
getProcessDpiAwarenessInternal = PhGetModuleProcAddress(L"user32.dll", "GetProcessDpiAwarenessInternal");
PhEndInitOnce(&initOnce);
}
if (!getProcessDpiAwarenessInternal)
return;
if (!(ProcessNode->ValidMask & PHPN_DPIAWARENESS))
{
if (ProcessNode->ProcessItem->QueryHandle)
{
ULONG dpiAwareness;
if (getProcessDpiAwarenessInternal(ProcessNode->ProcessItem->QueryHandle, &dpiAwareness))
ProcessNode->DpiAwareness = dpiAwareness + 1;
}
ProcessNode->ValidMask |= PHPN_DPIAWARENESS;
}
}
static VOID PhpUpdateNeedCyclesInformation(
VOID
)
{
PH_TREENEW_COLUMN column;
NeedCyclesInformation = FALSE;
// Before Windows Vista, there is no cycle time measurement.
// On Windows 7 and above, cycle time information is available directly from the process item.
// We only need to query cycle time separately for Windows Vista.
if (WindowsVersion != WINDOWS_VISTA)
return;
TreeNew_GetColumn(ProcessTreeListHandle, PHPRTLC_CYCLES, &column);
if (column.Visible)
{
NeedCyclesInformation = TRUE;
return;
}
TreeNew_GetColumn(ProcessTreeListHandle, PHPRTLC_CYCLESDELTA, &column);
if (column.Visible)
{
NeedCyclesInformation = TRUE;
return;
}
}
static VOID PhpUpdateProcessNodeCycles(
_Inout_ PPH_PROCESS_NODE ProcessNode
)
{
if (ProcessNode->ProcessId == SYSTEM_IDLE_PROCESS_ID)
{
PULARGE_INTEGER idleThreadCycleTimes;
ULONG64 cycleTime;
ULONG i;
// System Idle Process requires special treatment.
idleThreadCycleTimes = PhAllocate(
sizeof(ULARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors
);
if (NT_SUCCESS(NtQuerySystemInformation(
SystemProcessorIdleCycleTimeInformation,
idleThreadCycleTimes,
sizeof(ULARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors,
NULL
)))
{
cycleTime = 0;
for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++)
cycleTime += idleThreadCycleTimes[i].QuadPart;
PhUpdateDelta(&ProcessNode->CyclesDelta, cycleTime);
}
PhFree(idleThreadCycleTimes);
}
else if (ProcessNode->ProcessItem->QueryHandle)
{
ULONG64 cycleTime;
if (NT_SUCCESS(PhGetProcessCycleTime(ProcessNode->ProcessItem->QueryHandle, &cycleTime)))
{
PhUpdateDelta(&ProcessNode->CyclesDelta, cycleTime);
}
}
if (ProcessNode->CyclesDelta.Value != 0)
PhMoveReference(&ProcessNode->CyclesText, PhFormatUInt64(ProcessNode->CyclesDelta.Value, TRUE));
else
PhClearReference(&ProcessNode->CyclesText);
if (ProcessNode->CyclesDelta.Delta != 0)
PhMoveReference(&ProcessNode->CyclesDeltaText, PhFormatUInt64(ProcessNode->CyclesDelta.Delta, TRUE));
else
PhClearReference(&ProcessNode->CyclesDeltaText);
}
#define SORT_FUNCTION(Column) PhpProcessTreeNewCompare##Column
#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpProcessTreeNewCompare##Column( \
_In_ const void *_elem1, \
_In_ const void *_elem2 \
) \
{ \
PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)_elem1; \
PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)_elem2; \
PPH_PROCESS_ITEM processItem1 = node1->ProcessItem; \
PPH_PROCESS_ITEM processItem2 = node2->ProcessItem; \
int sortResult = 0;
#define END_SORT_FUNCTION \
if (sortResult == 0) \
sortResult = intptrcmp((LONG_PTR)processItem1->ProcessId, (LONG_PTR)processItem2->ProcessId); \
\
return PhModifySort(sortResult, ProcessTreeListSortOrder); \
}
LONG PhpProcessTreeNewPostSortFunction(
_In_ LONG Result,
_In_ PVOID Node1,
_In_ PVOID Node2,
_In_ PH_SORT_ORDER SortOrder
)
{
if (Result == 0)
Result = intptrcmp((LONG_PTR)((PPH_PROCESS_NODE)Node1)->ProcessItem->ProcessId, (LONG_PTR)((PPH_PROCESS_NODE)Node2)->ProcessItem->ProcessId);
return PhModifySort(Result, SortOrder);
}
BEGIN_SORT_FUNCTION(Name)
{
sortResult = PhCompareString(processItem1->ProcessName, processItem2->ProcessName, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Pid)
{
// Use signed int so DPCs and Interrupts are placed above System Idle Process.
sortResult = intptrcmp((LONG_PTR)processItem1->ProcessId, (LONG_PTR)processItem2->ProcessId);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Cpu)
{
sortResult = singlecmp(processItem1->CpuUsage, processItem2->CpuUsage);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoTotalRate)
{
sortResult = uint64cmp(
processItem1->IoReadDelta.Delta + processItem1->IoWriteDelta.Delta + processItem1->IoOtherDelta.Delta,
processItem2->IoReadDelta.Delta + processItem2->IoWriteDelta.Delta + processItem2->IoOtherDelta.Delta
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PrivateBytes)
{
sortResult = uintptrcmp(processItem1->VmCounters.PagefileUsage, processItem2->VmCounters.PagefileUsage);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(UserName)
{
sortResult = PhCompareStringWithNull(processItem1->UserName, processItem2->UserName, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Description)
{
PH_STRINGREF sr1;
PH_STRINGREF sr2;
sr1 = processItem1->VersionInfo.FileDescription ? processItem1->VersionInfo.FileDescription->sr : node1->DescriptionText;
sr2 = processItem2->VersionInfo.FileDescription ? processItem2->VersionInfo.FileDescription->sr : node2->DescriptionText;
sortResult = PhCompareStringRef(&sr1, &sr2, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(CompanyName)
{
sortResult = PhCompareStringWithNull(
processItem1->VersionInfo.CompanyName,
processItem2->VersionInfo.CompanyName,
TRUE
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Version)
{
sortResult = PhCompareStringWithNull(
processItem1->VersionInfo.FileVersion,
processItem2->VersionInfo.FileVersion,
TRUE
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(FileName)
{
sortResult = PhCompareStringWithNull(
processItem1->FileName,
processItem2->FileName,
TRUE
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(CommandLine)
{
sortResult = PhCompareStringWithNull(
processItem1->CommandLine,
processItem2->CommandLine,
TRUE
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PeakPrivateBytes)
{
sortResult = uintptrcmp(processItem1->VmCounters.PeakPagefileUsage, processItem2->VmCounters.PeakPagefileUsage);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(WorkingSet)
{
sortResult = uintptrcmp(processItem1->VmCounters.WorkingSetSize, processItem2->VmCounters.WorkingSetSize);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PeakWorkingSet)
{
sortResult = uintptrcmp(processItem1->VmCounters.PeakWorkingSetSize, processItem2->VmCounters.PeakWorkingSetSize);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PrivateWs)
{
PhpUpdateProcessNodeWsCounters(node1);
PhpUpdateProcessNodeWsCounters(node2);
sortResult = uintptrcmp(node1->WsCounters.NumberOfPrivatePages, node2->WsCounters.NumberOfPrivatePages);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PrivateWsWin7)
{
sortResult = uintptrcmp(processItem1->WorkingSetPrivateSize, processItem2->WorkingSetPrivateSize);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(SharedWs)
{
PhpUpdateProcessNodeWsCounters(node1);
PhpUpdateProcessNodeWsCounters(node2);
sortResult = uintptrcmp(node1->WsCounters.NumberOfSharedPages, node2->WsCounters.NumberOfSharedPages);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(ShareableWs)
{
PhpUpdateProcessNodeWsCounters(node1);
PhpUpdateProcessNodeWsCounters(node2);
sortResult = uintptrcmp(node1->WsCounters.NumberOfShareablePages, node2->WsCounters.NumberOfShareablePages);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(VirtualSize)
{
sortResult = uintptrcmp(processItem1->VmCounters.VirtualSize, processItem2->VmCounters.VirtualSize);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PeakVirtualSize)
{
sortResult = uintptrcmp(processItem1->VmCounters.PeakVirtualSize, processItem2->VmCounters.PeakVirtualSize);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PageFaults)
{
sortResult = uintcmp(processItem1->VmCounters.PageFaultCount, processItem2->VmCounters.PageFaultCount);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(SessionId)
{
sortResult = uintcmp(processItem1->SessionId, processItem2->SessionId);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(BasePriority)
{
sortResult = intcmp(processItem1->BasePriority, processItem2->BasePriority);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Threads)
{
sortResult = uintcmp(processItem1->NumberOfThreads, processItem2->NumberOfThreads);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Handles)
{
sortResult = uintcmp(processItem1->NumberOfHandles, processItem2->NumberOfHandles);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(GdiHandles)
{
sortResult = uintcmp(node1->GdiHandles, node2->GdiHandles);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(UserHandles)
{
sortResult = uintcmp(node1->UserHandles, node2->UserHandles);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoRoRate)
{
sortResult = uint64cmp(
processItem1->IoReadDelta.Delta + processItem1->IoOtherDelta.Delta,
processItem2->IoReadDelta.Delta + processItem2->IoOtherDelta.Delta
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoWRate)
{
sortResult = uint64cmp(
processItem1->IoWriteDelta.Delta,
processItem2->IoWriteDelta.Delta
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Integrity)
{
sortResult = uintcmp(processItem1->IntegrityLevel, processItem2->IntegrityLevel);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoPriority)
{
sortResult = uintcmp(node1->IoPriority, node2->IoPriority);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PagePriority)
{
sortResult = uintcmp(node1->PagePriority, node2->PagePriority);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(StartTime)
{
sortResult = int64cmp(processItem1->CreateTime.QuadPart, processItem2->CreateTime.QuadPart);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(TotalCpuTime)
{
sortResult = uint64cmp(
processItem1->KernelTime.QuadPart + processItem1->UserTime.QuadPart,
processItem2->KernelTime.QuadPart + processItem2->UserTime.QuadPart
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(KernelCpuTime)
{
sortResult = uint64cmp(
processItem1->KernelTime.QuadPart,
processItem2->KernelTime.QuadPart
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(UserCpuTime)
{
sortResult = uint64cmp(
processItem1->UserTime.QuadPart,
processItem2->UserTime.QuadPart
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(VerificationStatus)
{
sortResult = intcmp(processItem1->VerifyResult, processItem2->VerifyResult);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(VerifiedSigner)
{
sortResult = PhCompareStringWithNull(
processItem1->VerifySignerName,
processItem2->VerifySignerName,
TRUE
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Aslr)
{
PhpUpdateProcessNodeImage(node1);
PhpUpdateProcessNodeImage(node2);
sortResult = intcmp(
node1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE,
node2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(RelativeStartTime)
{
sortResult = -int64cmp(processItem1->CreateTime.QuadPart, processItem2->CreateTime.QuadPart);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Bits)
{
sortResult = intcmp(processItem1->IsWow64Valid, processItem2->IsWow64Valid);
if (sortResult == 0)
sortResult = intcmp(processItem1->IsWow64, processItem2->IsWow64);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Elevation)
{
ULONG key1;
ULONG key2;
switch (processItem1->ElevationType)
{
case TokenElevationTypeFull:
key1 = 2;
break;
case TokenElevationTypeLimited:
key1 = 1;
break;
default:
key1 = 0;
break;
}
switch (processItem2->ElevationType)
{
case TokenElevationTypeFull:
key2 = 2;
break;
case TokenElevationTypeLimited:
key2 = 1;
break;
default:
key2 = 0;
break;
}
sortResult = intcmp(key1, key2);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(WindowTitle)
{
PhpUpdateProcessNodeWindow(node1);
PhpUpdateProcessNodeWindow(node2);
sortResult = PhCompareStringWithNull(node1->WindowText, node2->WindowText, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(WindowStatus)
{
PhpUpdateProcessNodeWindow(node1);
PhpUpdateProcessNodeWindow(node2);
sortResult = intcmp(node1->WindowHung, node2->WindowHung);
// Make sure all processes with windows get grouped together.
if (sortResult == 0)
sortResult = intcmp(!!node1->WindowHandle, !!node2->WindowHandle);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Cycles)
{
sortResult = uint64cmp(node1->CyclesDelta.Value, node2->CyclesDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(CyclesWin7)
{
sortResult = uint64cmp(processItem1->CycleTimeDelta.Value, processItem2->CycleTimeDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(CyclesDelta)
{
sortResult = uint64cmp(node1->CyclesDelta.Delta, node2->CyclesDelta.Delta);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(CyclesDeltaWin7)
{
sortResult = uint64cmp(processItem1->CycleTimeDelta.Delta, processItem2->CycleTimeDelta.Delta);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(DepStatus)
{
PhpUpdateProcessNodeDepStatus(node1);
PhpUpdateProcessNodeDepStatus(node2);
sortResult = uintcmp(node1->DepStatus, node2->DepStatus);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Virtualized)
{
PhpUpdateProcessNodeToken(node1);
PhpUpdateProcessNodeToken(node2);
sortResult = intcmp(node1->VirtualizationEnabled, node2->VirtualizationEnabled);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(ContextSwitches)
{
sortResult = uintcmp(processItem1->ContextSwitchesDelta.Value, processItem2->ContextSwitchesDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(ContextSwitchesDelta)
{
sortResult = intcmp((LONG)processItem1->ContextSwitchesDelta.Delta, (LONG)processItem2->ContextSwitchesDelta.Delta);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PageFaultsDelta)
{
sortResult = uintcmp(processItem1->PageFaultsDelta.Delta, processItem2->PageFaultsDelta.Delta);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoReads)
{
sortResult = uint64cmp(processItem1->IoReadCountDelta.Value, processItem2->IoReadCountDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoWrites)
{
sortResult = uint64cmp(processItem1->IoWriteCountDelta.Value, processItem2->IoWriteCountDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoOther)
{
sortResult = uint64cmp(processItem1->IoOtherCountDelta.Value, processItem2->IoOtherCountDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoReadBytes)
{
sortResult = uint64cmp(processItem1->IoReadDelta.Value, processItem2->IoReadDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoWriteBytes)
{
sortResult = uint64cmp(processItem1->IoWriteDelta.Value, processItem2->IoWriteDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoOtherBytes)
{
sortResult = uint64cmp(processItem1->IoOtherDelta.Value, processItem2->IoOtherDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoReadsDelta)
{
sortResult = uint64cmp(processItem1->IoReadCountDelta.Delta, processItem2->IoReadCountDelta.Delta);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoWritesDelta)
{
sortResult = uint64cmp(processItem1->IoWriteCountDelta.Delta, processItem2->IoWriteCountDelta.Delta);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(IoOtherDelta)
{
sortResult = uint64cmp(processItem1->IoOtherCountDelta.Delta, processItem2->IoOtherCountDelta.Delta);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(OsContext)
{
PhpUpdateProcessOsContext(node1);
PhpUpdateProcessOsContext(node2);
sortResult = uintcmp(node1->OsContextVersion, node2->OsContextVersion);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PagedPool)
{
sortResult = uintptrcmp(processItem1->VmCounters.QuotaPagedPoolUsage, processItem2->VmCounters.QuotaPagedPoolUsage);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PeakPagedPool)
{
sortResult = uintptrcmp(processItem1->VmCounters.QuotaPeakPagedPoolUsage, processItem2->VmCounters.QuotaPeakPagedPoolUsage);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(NonPagedPool)
{
sortResult = uintptrcmp(processItem1->VmCounters.QuotaNonPagedPoolUsage, processItem2->VmCounters.QuotaNonPagedPoolUsage);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PeakNonPagedPool)
{
sortResult = uintptrcmp(processItem1->VmCounters.QuotaPeakNonPagedPoolUsage, processItem2->VmCounters.QuotaPeakNonPagedPoolUsage);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(MinimumWorkingSet)
{
PhpUpdateProcessNodeQuotaLimits(node1);
PhpUpdateProcessNodeQuotaLimits(node2);
sortResult = uintptrcmp(node1->MinimumWorkingSetSize, node2->MinimumWorkingSetSize);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(MaximumWorkingSet)
{
PhpUpdateProcessNodeQuotaLimits(node1);
PhpUpdateProcessNodeQuotaLimits(node2);
sortResult = uintptrcmp(node1->MaximumWorkingSetSize, node2->MaximumWorkingSetSize);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PrivateBytesDelta)
{
sortResult = intptrcmp(processItem1->PrivateBytesDelta.Delta, processItem2->PrivateBytesDelta.Delta);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Subsystem)
{
PhpUpdateProcessNodeImage(node1);
PhpUpdateProcessNodeImage(node2);
sortResult = intcmp(node1->ImageSubsystem, node2->ImageSubsystem);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PackageName)
{
sortResult = PhCompareStringWithNull(processItem1->PackageFullName, processItem2->PackageFullName, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(AppId)
{
PhpUpdateProcessNodeAppId(node1);
PhpUpdateProcessNodeAppId(node2);
sortResult = PhCompareStringWithNull(node1->AppIdText, node2->AppIdText, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(DpiAwareness)
{
PhpUpdateProcessNodeDpiAwareness(node1);
PhpUpdateProcessNodeDpiAwareness(node2);
sortResult = uintcmp(node1->DpiAwareness, node2->DpiAwareness);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(CfGuard)
{
PhpUpdateProcessNodeImage(node1);
PhpUpdateProcessNodeImage(node2);
sortResult = intcmp(
node1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF,
node2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF
);
}
END_SORT_FUNCTION
BOOLEAN NTAPI PhpProcessTreeNewCallback(
_In_ HWND hwnd,
_In_ PH_TREENEW_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2,
_In_opt_ PVOID Context
)
{
PPH_PROCESS_NODE node;
if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &ProcessTreeListCm))
return TRUE;
switch (Message)
{
case TreeNewGetChildren:
{
PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
node = (PPH_PROCESS_NODE)getChildren->Node;
if (ProcessTreeListSortOrder == NoSortOrder)
{
if (!node)
{
getChildren->Children = (PPH_TREENEW_NODE *)ProcessNodeRootList->Items;
getChildren->NumberOfChildren = ProcessNodeRootList->Count;
}
else
{
getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items;
getChildren->NumberOfChildren = node->Children->Count;
}
}
else
{
if (!node)
{
static PVOID sortFunctions[] =
{
SORT_FUNCTION(Name),
SORT_FUNCTION(Pid),
SORT_FUNCTION(Cpu),
SORT_FUNCTION(IoTotalRate),
SORT_FUNCTION(PrivateBytes),
SORT_FUNCTION(UserName),
SORT_FUNCTION(Description),
SORT_FUNCTION(CompanyName),
SORT_FUNCTION(Version),
SORT_FUNCTION(FileName),
SORT_FUNCTION(CommandLine),
SORT_FUNCTION(PeakPrivateBytes),
SORT_FUNCTION(WorkingSet),
SORT_FUNCTION(PeakWorkingSet),
SORT_FUNCTION(PrivateWs),
SORT_FUNCTION(SharedWs),
SORT_FUNCTION(ShareableWs),
SORT_FUNCTION(VirtualSize),
SORT_FUNCTION(PeakVirtualSize),
SORT_FUNCTION(PageFaults),
SORT_FUNCTION(SessionId),
SORT_FUNCTION(BasePriority), // Priority Class
SORT_FUNCTION(BasePriority),
SORT_FUNCTION(Threads),
SORT_FUNCTION(Handles),
SORT_FUNCTION(GdiHandles),
SORT_FUNCTION(UserHandles),
SORT_FUNCTION(IoRoRate),
SORT_FUNCTION(IoWRate),
SORT_FUNCTION(Integrity),
SORT_FUNCTION(IoPriority),
SORT_FUNCTION(PagePriority),
SORT_FUNCTION(StartTime),
SORT_FUNCTION(TotalCpuTime),
SORT_FUNCTION(KernelCpuTime),
SORT_FUNCTION(UserCpuTime),
SORT_FUNCTION(VerificationStatus),
SORT_FUNCTION(VerifiedSigner),
SORT_FUNCTION(Aslr),
SORT_FUNCTION(RelativeStartTime),
SORT_FUNCTION(Bits),
SORT_FUNCTION(Elevation),
SORT_FUNCTION(WindowTitle),
SORT_FUNCTION(WindowStatus),
SORT_FUNCTION(Cycles),
SORT_FUNCTION(CyclesDelta),
SORT_FUNCTION(Cpu), // CPU History
SORT_FUNCTION(PrivateBytes), // Private Bytes History
SORT_FUNCTION(IoTotalRate), // I/O History
SORT_FUNCTION(DepStatus),
SORT_FUNCTION(Virtualized),
SORT_FUNCTION(ContextSwitches),
SORT_FUNCTION(ContextSwitchesDelta),
SORT_FUNCTION(PageFaultsDelta),
SORT_FUNCTION(IoReads),
SORT_FUNCTION(IoWrites),
SORT_FUNCTION(IoOther),
SORT_FUNCTION(IoReadBytes),
SORT_FUNCTION(IoWriteBytes),
SORT_FUNCTION(IoOtherBytes),
SORT_FUNCTION(IoReadsDelta),
SORT_FUNCTION(IoWritesDelta),
SORT_FUNCTION(IoOtherDelta),
SORT_FUNCTION(OsContext),
SORT_FUNCTION(PagedPool),
SORT_FUNCTION(PeakPagedPool),
SORT_FUNCTION(NonPagedPool),
SORT_FUNCTION(PeakNonPagedPool),
SORT_FUNCTION(MinimumWorkingSet),
SORT_FUNCTION(MaximumWorkingSet),
SORT_FUNCTION(PrivateBytesDelta),
SORT_FUNCTION(Subsystem),
SORT_FUNCTION(PackageName),
SORT_FUNCTION(AppId),
SORT_FUNCTION(DpiAwareness),
SORT_FUNCTION(CfGuard)
};
static PH_INITONCE initOnce = PH_INITONCE_INIT;
int (__cdecl *sortFunction)(const void *, const void *);
if (PhBeginInitOnce(&initOnce))
{
if (WindowsVersion >= WINDOWS_7)
{
sortFunctions[PHPRTLC_PRIVATEWS] = SORT_FUNCTION(PrivateWsWin7);
sortFunctions[PHPRTLC_CYCLES] = SORT_FUNCTION(CyclesWin7);
sortFunctions[PHPRTLC_CYCLESDELTA] = SORT_FUNCTION(CyclesDeltaWin7);
}
PhEndInitOnce(&initOnce);
}
if (!PhCmForwardSort(
(PPH_TREENEW_NODE *)ProcessNodeList->Items,
ProcessNodeList->Count,
ProcessTreeListSortColumn,
ProcessTreeListSortOrder,
&ProcessTreeListCm
))
{
if (ProcessTreeListSortColumn < PHPRTLC_MAXIMUM)
sortFunction = sortFunctions[ProcessTreeListSortColumn];
else
sortFunction = NULL;
if (sortFunction)
{
qsort(ProcessNodeList->Items, ProcessNodeList->Count, sizeof(PVOID), sortFunction);
}
}
getChildren->Children = (PPH_TREENEW_NODE *)ProcessNodeList->Items;
getChildren->NumberOfChildren = ProcessNodeList->Count;
}
}
}
return TRUE;
case TreeNewIsLeaf:
{
PPH_TREENEW_IS_LEAF isLeaf = Parameter1;
node = (PPH_PROCESS_NODE)isLeaf->Node;
if (ProcessTreeListSortOrder == NoSortOrder)
isLeaf->IsLeaf = node->Children->Count == 0;
else
isLeaf->IsLeaf = TRUE;
}
return TRUE;
case TreeNewGetCellText:
{
PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1;
PPH_PROCESS_ITEM processItem;
node = (PPH_PROCESS_NODE)getCellText->Node;
processItem = node->ProcessItem;
switch (getCellText->Id)
{
case PHPRTLC_NAME:
getCellText->Text = processItem->ProcessName->sr;
break;
case PHPRTLC_PID:
PhInitializeStringRefLongHint(&getCellText->Text, processItem->ProcessIdString);
break;
case PHPRTLC_CPU:
{
FLOAT cpuUsage = 0;
PhpAggregateFieldIfNeeded(node, AggregateTypeFloat, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuUsage), &cpuUsage);
cpuUsage *= 100;
if (cpuUsage >= 0.01)
{
PH_FORMAT format;
SIZE_T returnLength;
PhInitFormatF(&format, cpuUsage, 2);
if (PhFormatToBuffer(&format, 1, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength))
{
getCellText->Text.Buffer = node->CpuUsageText;
getCellText->Text.Length = returnLength - sizeof(WCHAR); // minus null terminator
}
}
else if (cpuUsage != 0 && PhCsShowCpuBelow001)
{
PH_FORMAT format[2];
SIZE_T returnLength;
PhInitFormatS(&format[0], L"< ");
PhInitFormatF(&format[1], 0.01, 2);
if (PhFormatToBuffer(format, 2, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength))
{
getCellText->Text.Buffer = node->CpuUsageText;
getCellText->Text.Length = returnLength - sizeof(WCHAR);
}
}
}
break;
case PHPRTLC_IOTOTALRATE:
{
ULONG64 number = 0;
if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value) // delta is wrong on first run of process provider
{
PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Delta), &number);
PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Delta), &number);
PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Delta), &number);
number *= 1000;
number /= PhCsUpdateInterval;
}
if (number != 0)
{
PH_FORMAT format[2];
PhInitFormatSize(&format[0], number);
PhInitFormatS(&format[1], L"/s");
PhMoveReference(&node->IoTotalRateText, PhFormat(format, 2, 0));
getCellText->Text = node->IoTotalRateText->sr;
}
}
break;
case PHPRTLC_PRIVATEBYTES:
{
SIZE_T value = 0;
PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PagefileUsage), &value);
PhMoveReference(&node->PrivateBytesText, PhFormatSize(value, -1));
getCellText->Text = node->PrivateBytesText->sr;
}
break;
case PHPRTLC_USERNAME:
getCellText->Text = PhGetStringRef(processItem->UserName);
break;
case PHPRTLC_DESCRIPTION:
if (processItem->VersionInfo.FileDescription)
getCellText->Text = processItem->VersionInfo.FileDescription->sr;
else
getCellText->Text = node->DescriptionText;
break;
case PHPRTLC_COMPANYNAME:
getCellText->Text = PhGetStringRef(processItem->VersionInfo.CompanyName);
break;
case PHPRTLC_VERSION:
getCellText->Text = PhGetStringRef(processItem->VersionInfo.FileVersion);
break;
case PHPRTLC_FILENAME:
getCellText->Text = PhGetStringRef(processItem->FileName);
break;
case PHPRTLC_COMMANDLINE:
getCellText->Text = PhGetStringRef(processItem->CommandLine);
break;
case PHPRTLC_PEAKPRIVATEBYTES:
PhMoveReference(&node->PeakPrivateBytesText, PhFormatSize(processItem->VmCounters.PeakPagefileUsage, -1));
getCellText->Text = node->PeakPrivateBytesText->sr;
break;
case PHPRTLC_WORKINGSET:
{
SIZE_T value = 0;
PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.WorkingSetSize), &value);
PhMoveReference(&node->WorkingSetText, PhFormatSize(value, -1));
getCellText->Text = node->WorkingSetText->sr;
}
break;
case PHPRTLC_PEAKWORKINGSET:
PhMoveReference(&node->PeakWorkingSetText, PhFormatSize(processItem->VmCounters.PeakWorkingSetSize, -1));
getCellText->Text = node->PeakWorkingSetText->sr;
break;
case PHPRTLC_PRIVATEWS:
if (WindowsVersion >= WINDOWS_7)
{
PhMoveReference(&node->PrivateWsText, PhFormatSize(processItem->WorkingSetPrivateSize, -1));
}
else
{
PhpUpdateProcessNodeWsCounters(node);
PhMoveReference(&node->PrivateWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfPrivatePages * PAGE_SIZE, -1));
}
getCellText->Text = node->PrivateWsText->sr;
break;
case PHPRTLC_SHAREDWS:
PhpUpdateProcessNodeWsCounters(node);
PhMoveReference(&node->SharedWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfSharedPages * PAGE_SIZE, -1));
getCellText->Text = node->SharedWsText->sr;
break;
case PHPRTLC_SHAREABLEWS:
PhpUpdateProcessNodeWsCounters(node);
PhMoveReference(&node->ShareableWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfShareablePages * PAGE_SIZE, -1));
getCellText->Text = node->ShareableWsText->sr;
break;
case PHPRTLC_VIRTUALSIZE:
PhMoveReference(&node->VirtualSizeText, PhFormatSize(processItem->VmCounters.VirtualSize, -1));
getCellText->Text = node->VirtualSizeText->sr;
break;
case PHPRTLC_PEAKVIRTUALSIZE:
PhMoveReference(&node->PeakVirtualSizeText, PhFormatSize(processItem->VmCounters.PeakVirtualSize, -1));
getCellText->Text = node->PeakVirtualSizeText->sr;
break;
case PHPRTLC_PAGEFAULTS:
PhMoveReference(&node->PageFaultsText, PhFormatUInt64(processItem->VmCounters.PageFaultCount, TRUE));
getCellText->Text = node->PageFaultsText->sr;
break;
case PHPRTLC_SESSIONID:
PhInitializeStringRefLongHint(&getCellText->Text, processItem->SessionIdString);
break;
case PHPRTLC_PRIORITYCLASS:
PhInitializeStringRefLongHint(&getCellText->Text, PhGetProcessPriorityClassString(processItem->PriorityClass));
break;
case PHPRTLC_BASEPRIORITY:
PhPrintInt32(node->BasePriorityText, processItem->BasePriority);
PhInitializeStringRefLongHint(&getCellText->Text, node->BasePriorityText);
break;
case PHPRTLC_THREADS:
{
ULONG value = 0;
PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, NumberOfThreads), &value);
PhpFormatInt32GroupDigits(value, node->ThreadsText, sizeof(node->ThreadsText), &getCellText->Text);
}
break;
case PHPRTLC_HANDLES:
{
ULONG value = 0;
PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, NumberOfHandles), &value);
PhpFormatInt32GroupDigits(value, node->HandlesText, sizeof(node->HandlesText), &getCellText->Text);
}
break;
case PHPRTLC_GDIHANDLES:
PhpUpdateProcessNodeGdiUserHandles(node);
PhpFormatInt32GroupDigits(node->GdiHandles, node->GdiHandlesText, sizeof(node->GdiHandlesText), &getCellText->Text);
break;
case PHPRTLC_USERHANDLES:
PhpUpdateProcessNodeGdiUserHandles(node);
PhpFormatInt32GroupDigits(node->UserHandles, node->UserHandlesText, sizeof(node->UserHandlesText), &getCellText->Text);
break;
case PHPRTLC_IORORATE:
{
ULONG64 number = 0;
if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value)
{
PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Delta), &number);
PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Delta), &number);
number *= 1000;
number /= PhCsUpdateInterval;
}
if (number != 0)
{
PH_FORMAT format[2];
PhInitFormatSize(&format[0], number);
PhInitFormatS(&format[1], L"/s");
PhMoveReference(&node->IoRoRateText, PhFormat(format, 2, 0));
getCellText->Text = node->IoRoRateText->sr;
}
}
break;
case PHPRTLC_IOWRATE:
{
ULONG64 number = 0;
if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value)
{
PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Delta), &number);
number *= 1000;
number /= PhCsUpdateInterval;
}
if (number != 0)
{
PH_FORMAT format[2];
PhInitFormatSize(&format[0], number);
PhInitFormatS(&format[1], L"/s");
PhMoveReference(&node->IoWRateText, PhFormat(format, 2, 0));
getCellText->Text = node->IoWRateText->sr;
}
}
break;
case PHPRTLC_INTEGRITY:
if (processItem->IntegrityString)
PhInitializeStringRefLongHint(&getCellText->Text, processItem->IntegrityString);
break;
case PHPRTLC_IOPRIORITY:
PhpUpdateProcessNodeIoPagePriority(node);
if (node->IoPriority != -1)
{
if (node->IoPriority < MaxIoPriorityTypes)
PhInitializeStringRefLongHint(&getCellText->Text, PhIoPriorityHintNames[node->IoPriority]);
}
break;
case PHPRTLC_PAGEPRIORITY:
PhpUpdateProcessNodeIoPagePriority(node);
if (node->PagePriority != -1)
{
PhPrintUInt32(node->PagePriorityText, node->PagePriority);
PhInitializeStringRefLongHint(&getCellText->Text, node->PagePriorityText);
}
break;
case PHPRTLC_STARTTIME:
{
SYSTEMTIME systemTime;
if (processItem->CreateTime.QuadPart != 0)
{
PhLargeIntegerToLocalSystemTime(&systemTime, &processItem->CreateTime);
PhMoveReference(&node->StartTimeText, PhFormatDateTime(&systemTime));
getCellText->Text = node->StartTimeText->sr;
}
}
break;
case PHPRTLC_TOTALCPUTIME:
PhPrintTimeSpan(node->TotalCpuTimeText,
processItem->KernelTime.QuadPart + processItem->UserTime.QuadPart,
PH_TIMESPAN_HMSM);
PhInitializeStringRefLongHint(&getCellText->Text, node->TotalCpuTimeText);
break;
case PHPRTLC_KERNELCPUTIME:
PhPrintTimeSpan(node->KernelCpuTimeText, processItem->KernelTime.QuadPart, PH_TIMESPAN_HMSM);
PhInitializeStringRefLongHint(&getCellText->Text, node->KernelCpuTimeText);
break;
case PHPRTLC_USERCPUTIME:
PhPrintTimeSpan(node->UserCpuTimeText, processItem->UserTime.QuadPart, PH_TIMESPAN_HMSM);
PhInitializeStringRefLongHint(&getCellText->Text, node->UserCpuTimeText);
break;
case PHPRTLC_VERIFICATIONSTATUS:
if (processItem->VerifyResult == VrTrusted)
PhInitializeStringRef(&getCellText->Text, L"Trusted");
break;
case PHPRTLC_VERIFIEDSIGNER:
getCellText->Text = PhGetStringRef(processItem->VerifySignerName);
break;
case PHPRTLC_ASLR:
PhpUpdateProcessNodeImage(node);
if (WindowsVersion >= WINDOWS_VISTA)
{
if (node->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
PhInitializeStringRef(&getCellText->Text, L"ASLR");
}
else
{
PhInitializeStringRef(&getCellText->Text, L"N/A");
}
break;
case PHPRTLC_RELATIVESTARTTIME:
{
if (processItem->CreateTime.QuadPart != 0)
{
LARGE_INTEGER currentTime;
PPH_STRING startTimeString;
PhQuerySystemTime(&currentTime);
startTimeString = PhFormatTimeSpanRelative(currentTime.QuadPart - processItem->CreateTime.QuadPart);
PhMoveReference(&node->RelativeStartTimeText, PhConcatStrings2(startTimeString->Buffer, L" ago"));
PhDereferenceObject(startTimeString);
getCellText->Text = node->RelativeStartTimeText->sr;
}
}
break;
case PHPRTLC_BITS:
#ifdef _WIN64
if (processItem->IsWow64Valid)
PhInitializeStringRef(&getCellText->Text, processItem->IsWow64 ? L"32" : L"64");
#else
PhInitializeStringRef(&getCellText->Text, L"32");
#endif
break;
case PHPRTLC_ELEVATION:
{
PWSTR type;
if (WINDOWS_HAS_UAC)
{
switch (processItem->ElevationType)
{
case TokenElevationTypeDefault:
type = L"N/A";
break;
case TokenElevationTypeLimited:
type = L"Limited";
break;
case TokenElevationTypeFull:
type = L"Full";
break;
default:
type = L"N/A";
break;
}
}
else
{
type = L"";
}
PhInitializeStringRefLongHint(&getCellText->Text, type);
}
break;
case PHPRTLC_WINDOWTITLE:
PhpUpdateProcessNodeWindow(node);
PhSwapReference(&node->WindowTitleText, node->WindowText);
getCellText->Text = PhGetStringRef(node->WindowTitleText);
break;
case PHPRTLC_WINDOWSTATUS:
PhpUpdateProcessNodeWindow(node);
if (node->WindowHandle)
PhInitializeStringRef(&getCellText->Text, node->WindowHung ? L"Not responding" : L"Running");
break;
case PHPRTLC_CYCLES:
if (WindowsVersion >= WINDOWS_7)
{
ULONG64 value = 0;
PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Value), &value);
if (value != 0)
{
PhMoveReference(&node->CyclesText, PhFormatUInt64(value, TRUE));
getCellText->Text = node->CyclesText->sr;
}
}
else
{
getCellText->Text = PhGetStringRef(node->CyclesText);
}
break;
case PHPRTLC_CYCLESDELTA:
if (WindowsVersion >= WINDOWS_7)
{
ULONG64 value = 0;
PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Delta), &value);
if (value != 0)
{
PhMoveReference(&node->CyclesDeltaText, PhFormatUInt64(value, TRUE));
getCellText->Text = node->CyclesDeltaText->sr;
}
}
else
{
getCellText->Text = PhGetStringRef(node->CyclesDeltaText);
}
break;
case PHPRTLC_DEPSTATUS:
PhpUpdateProcessNodeDepStatus(node);
if (node->DepStatus & PH_PROCESS_DEP_ENABLED)
{
if (node->DepStatus & PH_PROCESS_DEP_PERMANENT)
PhInitializeStringRef(&getCellText->Text, L"DEP (Permanent)");
else
PhInitializeStringRef(&getCellText->Text, L"DEP");
}
break;
case PHPRTLC_VIRTUALIZED:
PhpUpdateProcessNodeToken(node);
if (node->VirtualizationEnabled)
PhInitializeStringRef(&getCellText->Text, L"Virtualized");
break;
case PHPRTLC_CONTEXTSWITCHES:
if (processItem->ContextSwitchesDelta.Value != 0)
{
PhMoveReference(&node->ContextSwitchesText, PhFormatUInt64(processItem->ContextSwitchesDelta.Value, TRUE));
getCellText->Text = node->ContextSwitchesText->sr;
}
break;
case PHPRTLC_CONTEXTSWITCHESDELTA:
if ((LONG)processItem->ContextSwitchesDelta.Delta > 0) // the delta may be negative if a thread exits - just don't show anything
{
PhMoveReference(&node->ContextSwitchesDeltaText, PhFormatUInt64(processItem->ContextSwitchesDelta.Delta, TRUE));
getCellText->Text = node->ContextSwitchesDeltaText->sr;
}
break;
case PHPRTLC_PAGEFAULTSDELTA:
if (processItem->PageFaultsDelta.Delta != 0)
{
PhMoveReference(&node->PageFaultsDeltaText, PhFormatUInt64(processItem->PageFaultsDelta.Delta, TRUE));
getCellText->Text = node->PageFaultsDeltaText->sr;
}
break;
case PHPRTLC_IOREADS:
if (processItem->IoReadCountDelta.Value != 0)
{
PhMoveReference(&node->IoGroupText[0], PhFormatUInt64(processItem->IoReadCountDelta.Value, TRUE));
getCellText->Text = node->IoGroupText[0]->sr;
}
break;
case PHPRTLC_IOWRITES:
if (processItem->IoWriteCountDelta.Value != 0)
{
PhMoveReference(&node->IoGroupText[1], PhFormatUInt64(processItem->IoWriteCountDelta.Value, TRUE));
getCellText->Text = node->IoGroupText[1]->sr;
}
break;
case PHPRTLC_IOOTHER:
if (processItem->IoOtherCountDelta.Value != 0)
{
PhMoveReference(&node->IoGroupText[2], PhFormatUInt64(processItem->IoOtherCountDelta.Value, TRUE));
getCellText->Text = node->IoGroupText[2]->sr;
}
break;
case PHPRTLC_IOREADBYTES:
if (processItem->IoReadDelta.Value != 0)
{
PhMoveReference(&node->IoGroupText[3], PhFormatSize(processItem->IoReadDelta.Value, -1));
getCellText->Text = node->IoGroupText[3]->sr;
}
break;
case PHPRTLC_IOWRITEBYTES:
if (processItem->IoWriteDelta.Value != 0)
{
PhMoveReference(&node->IoGroupText[4], PhFormatSize(processItem->IoWriteDelta.Value, -1));
getCellText->Text = node->IoGroupText[4]->sr;
}
break;
case PHPRTLC_IOOTHERBYTES:
if (processItem->IoOtherDelta.Value != 0)
{
PhMoveReference(&node->IoGroupText[5], PhFormatSize(processItem->IoOtherDelta.Value, -1));
getCellText->Text = node->IoGroupText[5]->sr;
}
break;
case PHPRTLC_IOREADSDELTA:
if (processItem->IoReadCountDelta.Delta != 0)
{
PhMoveReference(&node->IoGroupText[6], PhFormatUInt64(processItem->IoReadCountDelta.Delta, TRUE));
getCellText->Text = node->IoGroupText[6]->sr;
}
break;
case PHPRTLC_IOWRITESDELTA:
if (processItem->IoWriteCountDelta.Delta != 0)
{
PhMoveReference(&node->IoGroupText[7], PhFormatUInt64(processItem->IoWriteCountDelta.Delta, TRUE));
getCellText->Text = node->IoGroupText[7]->sr;
}
break;
case PHPRTLC_IOOTHERDELTA:
if (processItem->IoOtherCountDelta.Delta != 0)
{
PhMoveReference(&node->IoGroupText[8], PhFormatUInt64(processItem->IoOtherCountDelta.Delta, TRUE));
getCellText->Text = node->IoGroupText[8]->sr;
}
break;
case PHPRTLC_OSCONTEXT:
PhpUpdateProcessOsContext(node);
if (WindowsVersion >= WINDOWS_7)
{
switch (node->OsContextVersion)
{
case WINDOWS_10:
PhInitializeStringRef(&getCellText->Text, L"Windows 10");
break;
case WINDOWS_8_1:
PhInitializeStringRef(&getCellText->Text, L"Windows 8.1");
break;
case WINDOWS_8:
PhInitializeStringRef(&getCellText->Text, L"Windows 8");
break;
case WINDOWS_7:
PhInitializeStringRef(&getCellText->Text, L"Windows 7");
break;
case WINDOWS_VISTA:
PhInitializeStringRef(&getCellText->Text, L"Windows Vista");
break;
case WINDOWS_XP:
PhInitializeStringRef(&getCellText->Text, L"Windows XP");
break;
}
}
else
{
PhInitializeStringRef(&getCellText->Text, L"N/A");
}
break;
case PHPRTLC_PAGEDPOOL:
PhMoveReference(&node->PagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPagedPoolUsage, -1));
getCellText->Text = node->PagedPoolText->sr;
break;
case PHPRTLC_PEAKPAGEDPOOL:
PhMoveReference(&node->PeakPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPeakPagedPoolUsage, -1));
getCellText->Text = node->PeakPagedPoolText->sr;
break;
case PHPRTLC_NONPAGEDPOOL:
PhMoveReference(&node->NonPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaNonPagedPoolUsage, -1));
getCellText->Text = node->NonPagedPoolText->sr;
break;
case PHPRTLC_PEAKNONPAGEDPOOL:
PhMoveReference(&node->PeakNonPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPeakNonPagedPoolUsage, -1));
getCellText->Text = node->PeakNonPagedPoolText->sr;
break;
case PHPRTLC_MINIMUMWORKINGSET:
PhpUpdateProcessNodeQuotaLimits(node);
PhMoveReference(&node->MinimumWorkingSetText, PhFormatSize(node->MinimumWorkingSetSize, -1));
getCellText->Text = node->MinimumWorkingSetText->sr;
break;
case PHPRTLC_MAXIMUMWORKINGSET:
PhpUpdateProcessNodeQuotaLimits(node);
PhMoveReference(&node->MaximumWorkingSetText, PhFormatSize(node->MaximumWorkingSetSize, -1));
getCellText->Text = node->MaximumWorkingSetText->sr;
break;
case PHPRTLC_PRIVATEBYTESDELTA:
{
LONG_PTR delta;
delta = processItem->PrivateBytesDelta.Delta;
if (delta != 0)
{
PH_FORMAT format[2];
if (delta > 0)
{
PhInitFormatC(&format[0], '+');
}
else
{
PhInitFormatC(&format[0], '-');
delta = -delta;
}
format[1].Type = SizeFormatType | FormatUseRadix;
format[1].Radix = (UCHAR)PhMaxSizeUnit;
format[1].u.Size = delta;
PhMoveReference(&node->PrivateBytesDeltaText, PhFormat(format, 2, 0));
getCellText->Text = node->PrivateBytesDeltaText->sr;
}
}
break;
case PHPRTLC_SUBSYSTEM:
PhpUpdateProcessNodeImage(node);
switch (node->ImageSubsystem)
{
case 0:
break;
case IMAGE_SUBSYSTEM_NATIVE:
PhInitializeStringRef(&getCellText->Text, L"Native");
break;
case IMAGE_SUBSYSTEM_WINDOWS_GUI:
PhInitializeStringRef(&getCellText->Text, L"Windows");
break;
case IMAGE_SUBSYSTEM_WINDOWS_CUI:
PhInitializeStringRef(&getCellText->Text, L"Windows Console");
break;
case IMAGE_SUBSYSTEM_OS2_CUI:
PhInitializeStringRef(&getCellText->Text, L"OS/2");
break;
case IMAGE_SUBSYSTEM_POSIX_CUI:
PhInitializeStringRef(&getCellText->Text, L"POSIX");
break;
default:
PhInitializeStringRef(&getCellText->Text, L"Unknown");
break;
}
break;
case PHPRTLC_PACKAGENAME:
getCellText->Text = PhGetStringRef(processItem->PackageFullName);
break;
case PHPRTLC_APPID:
PhpUpdateProcessNodeAppId(node);
getCellText->Text = PhGetStringRef(node->AppIdText);
break;
case PHPRTLC_DPIAWARENESS:
PhpUpdateProcessNodeDpiAwareness(node);
switch (node->DpiAwareness)
{
case 0:
break;
case 1:
PhInitializeStringRef(&getCellText->Text, L"Unaware");
break;
case 2:
PhInitializeStringRef(&getCellText->Text, L"System Aware");
break;
case 3:
PhInitializeStringRef(&getCellText->Text, L"Per-Monitor Aware");
break;
}
break;
case PHPRTLC_CFGUARD:
PhpUpdateProcessNodeImage(node);
if (WindowsVersion >= WINDOWS_8_1)
{
if (node->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF)
PhInitializeStringRef(&getCellText->Text, L"CF Guard");
}
else
{
PhInitializeStringRef(&getCellText->Text, L"N/A");
}
break;
default:
return FALSE;
}
getCellText->Flags = TN_CACHE;
}
return TRUE;
case TreeNewGetNodeColor:
{
PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1;
PPH_PROCESS_ITEM processItem;
node = (PPH_PROCESS_NODE)getNodeColor->Node;
processItem = node->ProcessItem;
if (PhPluginsEnabled)
{
PH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor;
getHighlightingColor.Parameter = processItem;
getHighlightingColor.BackColor = RGB(0xff, 0xff, 0xff);
getHighlightingColor.Handled = FALSE;
getHighlightingColor.Cache = FALSE;
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), &getHighlightingColor);
if (getHighlightingColor.Handled)
{
getNodeColor->BackColor = getHighlightingColor.BackColor;
getNodeColor->Flags = TN_AUTO_FORECOLOR;
if (getHighlightingColor.Cache)
getNodeColor->Flags |= TN_CACHE;
return TRUE;
}
}
if (!processItem)
; // Dummy
else if (PhCsUseColorDebuggedProcesses && processItem->IsBeingDebugged)
getNodeColor->BackColor = PhCsColorDebuggedProcesses;
else if (PhCsUseColorSuspended && processItem->IsSuspended)
getNodeColor->BackColor = PhCsColorSuspended;
else if (PhCsUseColorElevatedProcesses && processItem->IsElevated)
getNodeColor->BackColor = PhCsColorElevatedProcesses;
else if (PhCsUseColorPosixProcesses && processItem->IsPosix)
getNodeColor->BackColor = PhCsColorPosixProcesses;
else if (PhCsUseColorJobProcesses && processItem->IsInSignificantJob)
getNodeColor->BackColor = PhCsColorJobProcesses;
else if (PhCsUseColorImmersiveProcesses && processItem->IsImmersive)
getNodeColor->BackColor = PhCsColorImmersiveProcesses;
else if (PhCsUseColorDotNet && processItem->IsDotNet)
getNodeColor->BackColor = PhCsColorDotNet;
else if (PhCsUseColorPacked && processItem->IsPacked)
getNodeColor->BackColor = PhCsColorPacked;
else if (PhCsUseColorWow64Processes && processItem->IsWow64)
getNodeColor->BackColor = PhCsColorWow64Processes;
else if (PhCsUseColorServiceProcesses && processItem->ServiceList && processItem->ServiceList->Count != 0)
getNodeColor->BackColor = PhCsColorServiceProcesses;
else if (
PhCsUseColorSystemProcesses &&
processItem->UserName &&
PhEqualString(processItem->UserName, PhLocalSystemName, TRUE)
)
getNodeColor->BackColor = PhCsColorSystemProcesses;
else if (
PhCsUseColorOwnProcesses &&
processItem->UserName &&
PhCurrentUserName &&
PhEqualString(processItem->UserName, PhCurrentUserName, TRUE)
)
getNodeColor->BackColor = PhCsColorOwnProcesses;
getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR;
}
return TRUE;
case TreeNewGetNodeIcon:
{
PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1;
node = (PPH_PROCESS_NODE)getNodeIcon->Node;
if (node->ProcessItem->SmallIcon)
{
getNodeIcon->Icon = node->ProcessItem->SmallIcon;
}
else
{
PhGetStockApplicationIcon(&getNodeIcon->Icon, NULL);
}
getNodeIcon->Flags = TN_CACHE;
}
return TRUE;
case TreeNewGetCellTooltip:
{
PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1;
ULONG tickCount;
node = (PPH_PROCESS_NODE)getCellTooltip->Node;
if (getCellTooltip->Column->Id != 0)
return FALSE;
tickCount = GetTickCount();
if ((LONG)(node->TooltipTextValidToTickCount - tickCount) < 0)
PhClearReference(&node->TooltipText);
if (!node->TooltipText)
node->TooltipText = PhGetProcessTooltipText(node->ProcessItem, &node->TooltipTextValidToTickCount);
if (!PhIsNullOrEmptyString(node->TooltipText))
{
getCellTooltip->Text = node->TooltipText->sr;
getCellTooltip->Unfolding = FALSE;
getCellTooltip->MaximumWidth = -1;
}
else
{
return FALSE;
}
}
return TRUE;
case TreeNewCustomDraw:
{
PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1;
PPH_PROCESS_ITEM processItem;
RECT rect;
PH_GRAPH_DRAW_INFO drawInfo;
node = (PPH_PROCESS_NODE)customDraw->Node;
processItem = node->ProcessItem;
rect = customDraw->CellRect;
if (rect.right - rect.left <= 1)
break; // nothing to draw
// Generic graph pre-processing
switch (customDraw->Column->Id)
{
case PHPRTLC_CPUHISTORY:
case PHPRTLC_PRIVATEBYTESHISTORY:
case PHPRTLC_IOHISTORY:
memset(&drawInfo, 0, sizeof(PH_GRAPH_DRAW_INFO));
drawInfo.Width = rect.right - rect.left - 1; // leave a small gap
drawInfo.Height = rect.bottom - rect.top - 1; // leave a small gap
drawInfo.Step = 2;
drawInfo.BackColor = RGB(0x00, 0x00, 0x00);
break;
}
// Specific graph processing
switch (customDraw->Column->Id)
{
case PHPRTLC_CPUHISTORY:
{
drawInfo.Flags = PH_GRAPH_USE_LINE_2;
drawInfo.LineColor1 = PhCsColorCpuKernel;
drawInfo.LineColor2 = PhCsColorCpuUser;
drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorCpuKernel);
drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorCpuUser);
PhGetDrawInfoGraphBuffers(
&node->CpuGraphBuffers,
&drawInfo,
processItem->CpuKernelHistory.Count
);
if (!node->CpuGraphBuffers.Valid)
{
PhCopyCircularBuffer_FLOAT(&processItem->CpuKernelHistory,
node->CpuGraphBuffers.Data1, drawInfo.LineDataCount);
PhCopyCircularBuffer_FLOAT(&processItem->CpuUserHistory,
node->CpuGraphBuffers.Data2, drawInfo.LineDataCount);
node->CpuGraphBuffers.Valid = TRUE;
}
}
break;
case PHPRTLC_PRIVATEBYTESHISTORY:
{
drawInfo.Flags = 0;
drawInfo.LineColor1 = PhCsColorPrivate;
drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorPrivate);
PhGetDrawInfoGraphBuffers(
&node->PrivateGraphBuffers,
&drawInfo,
processItem->PrivateBytesHistory.Count
);
if (!node->PrivateGraphBuffers.Valid)
{
ULONG i;
FLOAT total;
FLOAT max;
for (i = 0; i < drawInfo.LineDataCount; i++)
{
node->PrivateGraphBuffers.Data1[i] =
(FLOAT)PhGetItemCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory, i);
}
// This makes it easier for the user to see what processes are hogging memory.
// Scaling is still *not* consistent across all graphs.
total = (FLOAT)PhPerfInformation.CommittedPages * PAGE_SIZE / 4; // divide by 4 to make the scaling a bit better
max = (FLOAT)processItem->VmCounters.PeakPagefileUsage;
if (max < total)
max = total;
if (max != 0)
{
// Scale the data.
PhDivideSinglesBySingle(
node->PrivateGraphBuffers.Data1,
max,
drawInfo.LineDataCount
);
}
node->PrivateGraphBuffers.Valid = TRUE;
}
}
break;
case PHPRTLC_IOHISTORY:
{
drawInfo.Flags = PH_GRAPH_USE_LINE_2;
drawInfo.LineColor1 = PhCsColorIoReadOther;
drawInfo.LineColor2 = PhCsColorIoWrite;
drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorIoReadOther);
drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorIoWrite);
PhGetDrawInfoGraphBuffers(
&node->IoGraphBuffers,
&drawInfo,
processItem->IoReadHistory.Count
);
if (!node->IoGraphBuffers.Valid)
{
ULONG i;
FLOAT total;
FLOAT max = 0;
for (i = 0; i < drawInfo.LineDataCount; i++)
{
FLOAT data1;
FLOAT data2;
node->IoGraphBuffers.Data1[i] = data1 =
(FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoReadHistory, i) +
(FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoOtherHistory, i);
node->IoGraphBuffers.Data2[i] = data2 =
(FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoWriteHistory, i);
if (max < data1 + data2)
max = data1 + data2;
}
// Make the scaling a bit more consistent across the processes.
// It does *not* scale all graphs using the same maximum.
total = (FLOAT)(PhIoReadDelta.Delta + PhIoWriteDelta.Delta + PhIoOtherDelta.Delta);
if (max < total)
max = total;
if (max != 0)
{
// Scale the data.
PhDivideSinglesBySingle(
node->IoGraphBuffers.Data1,
max,
drawInfo.LineDataCount
);
PhDivideSinglesBySingle(
node->IoGraphBuffers.Data2,
max,
drawInfo.LineDataCount
);
}
node->IoGraphBuffers.Valid = TRUE;
}
}
break;
}
// Draw the graph.
switch (customDraw->Column->Id)
{
case PHPRTLC_CPUHISTORY:
case PHPRTLC_PRIVATEBYTESHISTORY:
case PHPRTLC_IOHISTORY:
PhpNeedGraphContext(customDraw->Dc, drawInfo.Width, drawInfo.Height);
if (GraphBits)
{
PhDrawGraphDirect(GraphContext, GraphBits, &drawInfo);
BitBlt(
customDraw->Dc,
rect.left,
rect.top + 1, // + 1 for small gap
drawInfo.Width,
drawInfo.Height,
GraphContext,
0,
0,
SRCCOPY
);
}
break;
}
}
return TRUE;
case TreeNewColumnResized:
{
PPH_TREENEW_COLUMN column = Parameter1;
ULONG i;
if (column->Id == PHPRTLC_CPUHISTORY || column->Id == PHPRTLC_IOHISTORY || column->Id == PHPRTLC_PRIVATEBYTESHISTORY)
{
for (i = 0; i < ProcessNodeList->Count; i++)
{
node = ProcessNodeList->Items[i];
if (column->Id == PHPRTLC_CPUHISTORY)
node->CpuGraphBuffers.Valid = FALSE;
if (column->Id == PHPRTLC_IOHISTORY)
node->IoGraphBuffers.Valid = FALSE;
if (column->Id == PHPRTLC_PRIVATEBYTESHISTORY)
node->PrivateGraphBuffers.Valid = FALSE;
}
}
}
return TRUE;
case TreeNewSortChanged:
{
TreeNew_GetSort(hwnd, &ProcessTreeListSortColumn, &ProcessTreeListSortOrder);
// 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(PhMainWndHandle, WM_COMMAND, ID_PROCESS_COPY, 0);
break;
case 'A':
if (GetKeyState(VK_CONTROL) < 0)
TreeNew_SelectRange(ProcessTreeListHandle, 0, -1);
break;
case VK_DELETE:
if (GetKeyState(VK_SHIFT) >= 0)
SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_TERMINATE, 0);
else
SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_TERMINATETREE, 0);
break;
case VK_RETURN:
if (GetKeyState(VK_CONTROL) >= 0)
SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_PROPERTIES, 0);
else
SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_OPENFILELOCATION, 0);
break;
}
}
return TRUE;
case TreeNewHeaderRightClick:
{
PH_TN_COLUMN_MENU_DATA data;
data.TreeNewHandle = hwnd;
data.MouseEvent = Parameter1;
data.DefaultSortColumn = 0;
data.DefaultSortOrder = NoSortOrder;
PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT);
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);
if (data.ProcessedId == PH_TN_COLUMN_MENU_HIDE_COLUMN_ID || data.ProcessedId == PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID)
PhpUpdateNeedCyclesInformation();
PhDeleteTreeNewColumnMenu(&data);
}
return TRUE;
case TreeNewLeftDoubleClick:
{
SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_PROPERTIES, 0);
}
return TRUE;
case TreeNewContextMenu:
{
PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1;
PhShowProcessContextMenu(contextMenu);
}
return TRUE;
case TreeNewNodeExpanding:
{
node = Parameter1;
if (PhCsPropagateCpuUsage)
PhUpdateProcessNode(node);
}
return TRUE;
}
return FALSE;
}
PPH_PROCESS_ITEM PhGetSelectedProcessItem(
VOID
)
{
PPH_PROCESS_ITEM processItem = NULL;
ULONG i;
for (i = 0; i < ProcessNodeList->Count; i++)
{
PPH_PROCESS_NODE node = ProcessNodeList->Items[i];
if (node->Node.Selected)
{
processItem = node->ProcessItem;
break;
}
}
return processItem;
}
VOID PhGetSelectedProcessItems(
_Out_ PPH_PROCESS_ITEM **Processes,
_Out_ PULONG NumberOfProcesses
)
{
PPH_LIST list;
ULONG i;
list = PhCreateList(2);
for (i = 0; i < ProcessNodeList->Count; i++)
{
PPH_PROCESS_NODE node = ProcessNodeList->Items[i];
if (node->Node.Selected)
{
PhAddItemList(list, node->ProcessItem);
}
}
*Processes = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
*NumberOfProcesses = list->Count;
PhDereferenceObject(list);
}
VOID PhDeselectAllProcessNodes(
VOID
)
{
TreeNew_DeselectRange(ProcessTreeListHandle, 0, -1);
}
VOID PhExpandAllProcessNodes(
_In_ BOOLEAN Expand
)
{
ULONG i;
BOOLEAN needsRestructure = FALSE;
for (i = 0; i < ProcessNodeList->Count; i++)
{
PPH_PROCESS_NODE node = ProcessNodeList->Items[i];
if (node->Children->Count != 0 && node->Node.Expanded != Expand)
{
node->Node.Expanded = Expand;
needsRestructure = TRUE;
}
}
if (needsRestructure)
TreeNew_NodesStructured(ProcessTreeListHandle);
}
VOID PhInvalidateAllProcessNodes(
VOID
)
{
ULONG i;
for (i = 0; i < ProcessNodeList->Count; i++)
{
PPH_PROCESS_NODE node = ProcessNodeList->Items[i];
memset(node->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM);
PhInvalidateTreeNewNode(&node->Node, TN_CACHE_COLOR);
node->ValidMask = 0;
// Invalidate graph buffers.
node->CpuGraphBuffers.Valid = FALSE;
node->PrivateGraphBuffers.Valid = FALSE;
node->IoGraphBuffers.Valid = FALSE;
}
InvalidateRect(ProcessTreeListHandle, NULL, FALSE);
}
VOID PhSelectAndEnsureVisibleProcessNode(
_In_ PPH_PROCESS_NODE ProcessNode
)
{
PhSelectAndEnsureVisibleProcessNodes(&ProcessNode, 1);
}
VOID PhSelectAndEnsureVisibleProcessNodes(
_In_ PPH_PROCESS_NODE *ProcessNodes,
_In_ ULONG NumberOfProcessNodes
)
{
ULONG i;
PPH_PROCESS_NODE leader = NULL;
PPH_PROCESS_NODE node;
BOOLEAN needsRestructure = FALSE;
PhDeselectAllProcessNodes();
for (i = 0; i < NumberOfProcessNodes; i++)
{
if (ProcessNodes[i]->Node.Visible)
{
leader = ProcessNodes[i];
break;
}
}
if (!leader)
return;
// Expand recursively upwards, and select the nodes.
for (i = 0; i < NumberOfProcessNodes; i++)
{
if (!ProcessNodes[i]->Node.Visible)
continue;
node = ProcessNodes[i]->Parent;
while (node)
{
if (!node->Node.Expanded)
needsRestructure = TRUE;
node->Node.Expanded = TRUE;
node = node->Parent;
}
ProcessNodes[i]->Node.Selected = TRUE;
}
if (needsRestructure)
TreeNew_NodesStructured(ProcessTreeListHandle);
TreeNew_SetFocusNode(ProcessTreeListHandle, &leader->Node);
TreeNew_SetMarkNode(ProcessTreeListHandle, &leader->Node);
TreeNew_EnsureVisible(ProcessTreeListHandle, &leader->Node);
TreeNew_InvalidateNode(ProcessTreeListHandle, &leader->Node);
}
VOID PhpPopulateTableWithProcessNodes(
_In_ HWND TreeListHandle,
_In_ PPH_PROCESS_NODE Node,
_In_ ULONG Level,
_In_ PPH_STRING **Table,
_Inout_ PULONG Index,
_In_ PULONG DisplayToId,
_In_ ULONG Columns
)
{
ULONG i;
for (i = 0; i < Columns; i++)
{
PH_TREENEW_GET_CELL_TEXT getCellText;
PPH_STRING text;
getCellText.Node = &Node->Node;
getCellText.Id = DisplayToId[i];
PhInitializeEmptyStringRef(&getCellText.Text);
TreeNew_GetCellText(TreeListHandle, &getCellText);
if (i != 0)
{
text = PhaCreateStringEx(getCellText.Text.Buffer, getCellText.Text.Length);
}
else
{
// If this is the first column in the row, add some indentation.
text = PhaCreateStringEx(
NULL,
getCellText.Text.Length + Level * 2 * sizeof(WCHAR)
);
wmemset(text->Buffer, ' ', Level * 2);
memcpy(&text->Buffer[Level * 2], getCellText.Text.Buffer, getCellText.Text.Length);
}
Table[*Index][i] = text;
}
(*Index)++;
// Process the children.
for (i = 0; i < Node->Children->Count; i++)
{
PhpPopulateTableWithProcessNodes(
TreeListHandle,
Node->Children->Items[i],
Level + 1,
Table,
Index,
DisplayToId,
Columns
);
}
}
PPH_LIST PhGetProcessTreeListLines(
_In_ HWND TreeListHandle,
_In_ ULONG NumberOfNodes,
_In_ PPH_LIST RootNodes,
_In_ ULONG Mode
)
{
PH_AUTO_POOL autoPool;
PPH_LIST lines;
// The number of rows in the table (including +1 for the column headers).
ULONG rows;
// The number of columns.
ULONG columns;
// A column display index to ID map.
PULONG displayToId;
// A column display index to text map.
PWSTR *displayToText;
// The actual string table.
PPH_STRING **table;
ULONG i;
ULONG j;
// Use a local auto-pool to make memory mangement a bit less painful.
PhInitializeAutoPool(&autoPool);
rows = NumberOfNodes + 1;
// Create the display index to ID map.
PhMapDisplayIndexTreeNew(TreeListHandle, &displayToId, &displayToText, &columns);
PhaCreateTextTable(&table, rows, columns);
// Populate the first row with the column headers.
for (i = 0; i < columns; i++)
{
table[0][i] = PhaCreateString(displayToText[i]);
}
// Go through the nodes in the process tree and populate each cell of the table.
j = 1; // index starts at one because the first row contains the column headers.
for (i = 0; i < RootNodes->Count; i++)
{
PhpPopulateTableWithProcessNodes(
TreeListHandle,
RootNodes->Items[i],
0,
table,
&j,
displayToId,
columns
);
}
PhFree(displayToId);
PhFree(displayToText);
lines = PhaFormatTextTable(table, rows, columns, Mode);
PhDeleteAutoPool(&autoPool);
return lines;
}
VOID PhCopyProcessTree(
VOID
)
{
PPH_STRING text;
text = PhGetTreeNewText(ProcessTreeListHandle, 0);
PhSetClipboardString(ProcessTreeListHandle, &text->sr);
PhDereferenceObject(text);
}
VOID PhWriteProcessTree(
_Inout_ PPH_FILE_STREAM FileStream,
_In_ ULONG Mode
)
{
PPH_LIST lines;
ULONG i;
lines = PhGetProcessTreeListLines(
ProcessTreeListHandle,
ProcessNodeList->Count,
ProcessNodeRootList,
Mode
);
for (i = 0; i < lines->Count; i++)
{
PPH_STRING line;
line = lines->Items[i];
PhWriteStringAsUtf8FileStream(FileStream, &line->sr);
PhDereferenceObject(line);
PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n");
}
PhDereferenceObject(lines);
}
PPH_LIST PhDuplicateProcessNodeList(
VOID
)
{
PPH_LIST newList;
newList = PhCreateList(ProcessNodeList->Count);
PhInsertItemsList(newList, 0, ProcessNodeList->Items, ProcessNodeList->Count);
return newList;
}