/*
 * Copyright (c) 2022 Winsider Seminars & Solutions, Inc.  All rights reserved.
 *
 * This file is part of System Informer.
 *
 * Authors:
 *
 *     wj32    2010-2016
 *     dmex    2016-2023
 *     jxy-s   2021
 *
 */

/*
 * 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 <proctree.h>

#include <appresolver.h>
#include <cpysave.h>
#include <emenu.h>
#include <hndlinfo.h>
#include <kphuser.h>
#include <mapimg.h>
#include <secedit.h>
#include <settings.h>
#include <verify.h>

#include <colmgr.h>
#include <extmgri.h>
#include <mainwnd.h>
#include <phplug.h>
#include <phsettings.h>
#include <procprv.h>
#include <procmtgn.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,
    _In_opt_ PVOID Context
    );

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
    );

BOOLEAN PhpShouldShowImageCoherency(
    _In_ PPH_PROCESS_ITEM ProcessItem,
    _In_ BOOLEAN CheckThreshold
    );

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
static PH_TN_FILTER_SUPPORT FilterSupport;

BOOLEAN PhProcessTreeListStateHighlighting = TRUE;
static PPH_POINTER_LIST ProcessNodeStateList = NULL; // list of nodes which need to be processed
static ULONG PhProcessTreeColumnHeaderCacheLength = 0;
static PVOID PhProcessTreeColumnHeaderCache = NULL;
static ULONG PhProcessTreeColumnHeaderTextCacheLength = 0;
static PVOID PhProcessTreeColumnHeaderTextCache = NULL;

static HDC GraphContext = NULL;
static ULONG GraphContextWidth = 0;
static ULONG GraphContextHeight = 0;
static HBITMAP GraphOldBitmap = NULL;
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_SetRedraw(hwnd, FALSE);
    TreeNew_SetExtendedFlags(hwnd, TN_FLAG_ITEM_DRAG_SELECT, TN_FLAG_ITEM_DRAG_SELECT);
    TreeNew_SetCallback(hwnd, PhpProcessTreeNewCallback, NULL);
    TreeNew_SetImageList(hwnd, PhProcessSmallImageList);
    TreeNew_SetMaxId(hwnd, PHPRTLC_MAXIMUM - 1);

    // Default columns
    PhAddTreeNewColumn(hwnd, PHPRTLC_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, (PhGetIntegerSetting(L"ProcessTreeListNameDefault") ? TN_COLUMN_FIXED : 0), 0); // HACK (dmex)
    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, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_VERSION, FALSE, L"Version", 100, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_FILENAME, FALSE, L"File name", 180, PH_ALIGN_LEFT, ULONG_MAX, DT_PATH_ELLIPSIS);
    PhAddTreeNewColumn(hwnd, PHPRTLC_COMMANDLINE, FALSE, L"Command line", 180, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKPRIVATEBYTES, FALSE, L"Peak private bytes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_WORKINGSET, FALSE, L"Working set", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKWORKINGSET, FALSE, L"Peak working set", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEWS, FALSE, L"Private WS", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_SHAREDWS, FALSE, L"Shared WS", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_SHAREABLEWS, FALSE, L"Shareable WS", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_VIRTUALSIZE, FALSE, L"Virtual size", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKVIRTUALSIZE, FALSE, L"Peak virtual size", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEFAULTS, FALSE, L"Page faults", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumn(hwnd, PHPRTLC_SESSIONID, FALSE, L"Session ID", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIORITYCLASS, FALSE, L"Priority class", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_BASEPRIORITY, FALSE, L"Base priority", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);

    // Customizable columns (2)
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_THREADS, FALSE, L"Threads", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_HANDLES, FALSE, L"Handles", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_GDIHANDLES, FALSE, L"GDI handles", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_USERHANDLES, FALSE, L"USER handles", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IORORATE, FALSE, L"I/O read+other rate", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRATE, FALSE, L"I/O write rate", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumn(hwnd, PHPRTLC_INTEGRITY, FALSE, L"Integrity", 100, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOPRIORITY, FALSE, L"I/O priority", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEPRIORITY, FALSE, L"Page priority", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_STARTTIME, FALSE, L"Start time", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_TOTALCPUTIME, FALSE, L"Total CPU time", 90, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_KERNELCPUTIME, FALSE, L"Kernel CPU time", 90, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_USERCPUTIME, FALSE, L"User CPU time", 90, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumn(hwnd, PHPRTLC_VERIFICATIONSTATUS, FALSE, L"Verification status", 70, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_VERIFIEDSIGNER, FALSE, L"Verified signer", 100, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_ASLR, FALSE, L"ASLR", 50, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumn(hwnd, PHPRTLC_RELATIVESTARTTIME, FALSE, L"Relative start time", 180, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_BITS, FALSE, L"Bits", 50, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_ELEVATION, FALSE, L"Elevation", 60, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumn(hwnd, PHPRTLC_WINDOWTITLE, FALSE, L"Window title", 120, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_WINDOWSTATUS, FALSE, L"Window status", 60, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CYCLES, FALSE, L"Cycles", 110, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CYCLESDELTA, FALSE, L"Cycles delta", 90, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx2(hwnd, PHPRTLC_CPUHISTORY, FALSE, L"CPU history", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING);
    PhAddTreeNewColumnEx2(hwnd, PHPRTLC_PRIVATEBYTESHISTORY, FALSE, L"Private bytes history", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING);
    PhAddTreeNewColumnEx2(hwnd, PHPRTLC_IOHISTORY, FALSE, L"I/O history", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING);
    PhAddTreeNewColumn(hwnd, PHPRTLC_DEP, FALSE, L"DEP", 100, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_VIRTUALIZED, FALSE, L"Virtualized", 80, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CONTEXTSWITCHES, FALSE, L"Context switches", 100, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CONTEXTSWITCHESDELTA, FALSE, L"Context switches delta", 80, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEFAULTSDELTA, FALSE, L"Page faults delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);

    // I/O group columns
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADS, FALSE, L"I/O reads", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITES, FALSE, L"I/O writes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHER, FALSE, L"I/O other", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADBYTES, FALSE, L"I/O read bytes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITEBYTES, FALSE, L"I/O write bytes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHERBYTES, FALSE, L"I/O other bytes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADSDELTA, FALSE, L"I/O reads delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITESDELTA, FALSE, L"I/O writes delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHERDELTA, FALSE, L"I/O other delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);

    // Customizable columns (3)
    PhAddTreeNewColumn(hwnd, PHPRTLC_OSCONTEXT, FALSE, L"OS context", 100, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEDPOOL, FALSE, L"Paged pool", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKPAGEDPOOL, FALSE, L"Peak paged pool", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_NONPAGEDPOOL, FALSE, L"Non-paged pool", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKNONPAGEDPOOL, FALSE, L"Peak non-paged pool", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_MINIMUMWORKINGSET, FALSE, L"Minimum working set", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_MAXIMUMWORKINGSET, FALSE, L"Maximum working set", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEBYTESDELTA, FALSE, L"Private bytes delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumn(hwnd, PHPRTLC_SUBSYSTEM, FALSE, L"Subsystem", 110, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_PACKAGENAME, FALSE, L"Package name", 160, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_APPID, FALSE, L"App ID", 160, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_DPIAWARENESS, FALSE, L"DPI awareness", 110, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CFGUARD, FALSE, L"CF Guard", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_TIMESTAMP, FALSE, L"Time stamp", 140, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_FILEMODIFIEDTIME, FALSE, L"File modified time", 140, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_FILESIZE, FALSE, L"File size", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_SUBPROCESSCOUNT, FALSE, L"Subprocesses", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_JOBOBJECTID, FALSE, L"Job Object ID", 50, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PROTECTION, FALSE, L"Protection", 105, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_DESKTOP, FALSE, L"Desktop", 80, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CRITICAL, FALSE, L"Critical", 80, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PIDHEX, FALSE, L"PID (Hex)", 50, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CPUCORECYCLES, FALSE, L"CPU (relative)", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CET, FALSE, L"CET", 45, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_IMAGE_COHERENCY, FALSE, L"Image coherency", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_ERRORMODE, FALSE, L"Error mode", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CODEPAGE, FALSE, L"Code page", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx2(hwnd, PHPRTLC_TIMELINE, FALSE, L"Timeline", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_POWERTHROTTLING, FALSE, L"Power throttling", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_ARCHITECTURE, FALSE, L"Architecture", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumn(hwnd, PHPRTLC_PARENTPID, FALSE, L"Parent PID", 50, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT);
    PhAddTreeNewColumn(hwnd, PHPRTLC_PARENTCONSOLEPID, FALSE, L"Parent console PID", 50, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_COMMITSIZE, FALSE, L"Shared commit", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIORITYBOOST, FALSE, L"Priority boost", 45, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CPUAVERAGE, FALSE, L"CPU (average)", 50, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CPUKERNEL, FALSE, L"CPU (kernel)", 50, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumnEx(hwnd, PHPRTLC_CPUUSER, FALSE, L"CPU (user)", 50, PH_ALIGN_LEFT, ULONG_MAX, DT_RIGHT, TRUE);
    PhAddTreeNewColumn(hwnd, PHPRTLC_GRANTEDACCESS, FALSE, L"Granted access (symbolic)", 50, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_TLSBITMAPDELTA, FALSE, L"Thread local storage", 50, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_REFERENCEDELTA, FALSE, L"References", 50, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_LXSSPID, FALSE, L"PID (LXSS)", 50, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_START_KEY, FALSE, L"Start key", 120, PH_ALIGN_LEFT, ULONG_MAX, 0);
    PhAddTreeNewColumn(hwnd, PHPRTLC_MITIGATION_POLICIES, FALSE, L"Mitigation policies", 180, PH_ALIGN_LEFT, ULONG_MAX, 0);

    PhCmInitializeManager(&ProcessTreeListCm, hwnd, PHPRTLC_MAXIMUM, PhpProcessTreeNewPostSortFunction);
    PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, ProcessNodeList);

    TreeNew_SetTriState(hwnd, TRUE);
    TreeNew_SetRedraw(hwnd, TRUE);

    if (PhPluginsEnabled)
    {
        PH_PLUGIN_TREENEW_INFORMATION treeNewInfo;

        treeNewInfo.TreeNewHandle = hwnd;
        treeNewInfo.CmData = &ProcessTreeListCm;
        PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), &treeNewInfo);
    }
}

VOID PhInitializeProcessTreeColumnHeaderCache(
    VOID
    )
{
    PH_TREENEW_SET_HEADER_CACHE processTreeColumnHeaderCache;
    ULONG columnCount = TreeNew_GetColumnCount(ProcessTreeListHandle);

    // TODO: This creates a text cache for all columns wasting space since
    // only some provide header text support. We should switch to the same treenode
    // caching where nodes provide the cache instead of the control. (dmex)

    PhProcessTreeColumnHeaderCacheLength = columnCount * sizeof(PH_STRINGREF);
    PhProcessTreeColumnHeaderCache = PhAllocateZero(PhProcessTreeColumnHeaderCacheLength);

    PhProcessTreeColumnHeaderTextCacheLength = columnCount * (PH_TREENEW_HEADER_TEXT_SIZE_MAX * sizeof(WCHAR));
    PhProcessTreeColumnHeaderTextCache = PhAllocateZero(PhProcessTreeColumnHeaderTextCacheLength);

    processTreeColumnHeaderCache.HeaderTreeColumnMax = columnCount;
    processTreeColumnHeaderCache.HeaderTreeColumnStringCache = PhProcessTreeColumnHeaderCache;
    processTreeColumnHeaderCache.HeaderTreeColumnTextCache = PhProcessTreeColumnHeaderTextCache;
    TreeNew_SetColumnTextCache(ProcessTreeListHandle, &processTreeColumnHeaderCache);
}

VOID PhLoadSettingsProcessTreeUpdateMask(
    VOID
    )
{
#ifdef PH_TREELIST_COLUMN_ARRAY
    PH_TREENEW_COLUMN column;

    if (TreeNew_GetColumn(ProcessTreeListHandle, PHPRTLC_USERNAME, &column) && column.Visible)
        SetFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_AVERAGE);
    else
        ClearFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_AVERAGE);
#else
    ULONG columnVisible[4] =
    {
        PHPRTLC_USERNAME,
        PHPRTLC_CFGUARD,
        PHPRTLC_CET,
        PHPRTLC_CPUAVERAGE
    };

    if (!TreeNew_GetVisibleColumnArray(ProcessTreeListHandle, RTL_NUMBER_OF(columnVisible), columnVisible))
        return;

    if (columnVisible[0])
        SetFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_USERNAME);
    else
        ClearFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_USERNAME);

    if (columnVisible[1])
        SetFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_CFGUARD);
    else
        ClearFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_CFGUARD);

    if (columnVisible[2])
        SetFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_CET);
    else
        ClearFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_CET);

    if (columnVisible[3])
        SetFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_AVERAGE);
    else
        ClearFlag(PhProcessProviderFlagsMask, PH_PROCESS_PROVIDER_FLAG_AVERAGE);
#endif

    PhInitializeProcessTreeColumnHeaderCache();
}

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);
    else
        SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT);

    PhLoadSettingsProcessTreeUpdateMask();
}

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 PhLoadSettingsProcessTreeListEx(
    _In_ PPH_STRING TreeListSettings,
    _In_ PPH_STRING TreeSortSettings
    )
{
    PhCmLoadSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &TreeListSettings->sr, &TreeSortSettings->sr);

    if (PhGetIntegerSetting(L"EnableInstantTooltips"))
        SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, 0);
    else
        SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT);

    PhLoadSettingsProcessTreeUpdateMask();
}

VOID PhSaveSettingsProcessTreeListEx(
    _Out_ PPH_STRING *TreeListSettings,
    _Out_ PPH_STRING *TreeSortSettings
    )
{
    PPH_STRING settings;
    PPH_STRING sortSettings;

    settings = PhCmSaveSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &sortSettings);

    *TreeListSettings = settings;
    *TreeSortSettings = sortSettings;
}

VOID PhReloadSettingsProcessTreeList(
    VOID
    )
{
    if (PhGetIntegerSetting(L"EnableInstantTooltips"))
        SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, 0);
    else
        SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT);

    PhLoadSettingsProcessTreeUpdateMask();
}

PPH_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 PhpValidateParentProcessNode(
    _In_ PPH_PROCESS_NODE Child,
    _In_ PPH_PROCESS_NODE Parent
    )
{
    if (WindowsVersion >= WINDOWS_10_RS3)
    {
        return PH_IS_FAKE_PROCESS_ID(Child->ProcessId) ||
            Parent->ProcessItem->ProcessSequenceNumber <= Child->ProcessItem->ProcessSequenceNumber;
    }
    else
    {
        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)) &&
        PhpValidateParentProcessNode(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 &&
            PhpValidateParentProcessNode(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 (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, NULL);
    }
}

VOID PhpRemoveProcessNode(
    _In_ PPH_PROCESS_NODE ProcessNode,
    _In_opt_ PVOID Context
    )
{
    ULONG index;
    ULONG i;

    PhEmCallObjectOperation(EmProcessNodeType, ProcessNode, EmObjectDelete);

    if (ProcessNode->Parent)
    {
        // Remove the node from its parent.

        if ((index = PhFindItemList(ProcessNode->Parent->Children, ProcessNode)) != ULONG_MAX)
            PhRemoveItemList(ProcessNode->Parent->Children, index);
    }
    else
    {
        // Remove the node from the root list.

        if ((index = PhFindItemList(ProcessNodeRootList, ProcessNode)) != ULONG_MAX)
            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)) != ULONG_MAX)
        PhRemoveItemList(ProcessNodeList, index);

    PhDereferenceObject(ProcessNode->Children);

    PhClearReference(&ProcessNode->WindowText);
    PhClearReference(&ProcessNode->AppIdText);

    PhClearReference(&ProcessNode->TooltipText);

    PhClearReference(&ProcessNode->IoTotalRateText);
    PhClearReference(&ProcessNode->PrivateBytesText);
    PhClearReference(&ProcessNode->PeakPrivateBytesText);
    PhClearReference(&ProcessNode->WorkingSetText);
    PhClearReference(&ProcessNode->PeakWorkingSetText);
    PhClearReference(&ProcessNode->PrivateWsText);
    PhClearReference(&ProcessNode->SharedWsText);
    PhClearReference(&ProcessNode->ShareableWsText);
    PhClearReference(&ProcessNode->VirtualSizeText);
    PhClearReference(&ProcessNode->PeakVirtualSizeText);
    PhClearReference(&ProcessNode->PageFaultsText);
    PhClearReference(&ProcessNode->SessionIdText);
    PhClearReference(&ProcessNode->BasePriorityText);
    PhClearReference(&ProcessNode->ThreadsText);
    PhClearReference(&ProcessNode->HandlesText);
    PhClearReference(&ProcessNode->GdiHandlesText);
    PhClearReference(&ProcessNode->UserHandlesText);
    PhClearReference(&ProcessNode->IoRoRateText);
    PhClearReference(&ProcessNode->IoWRateText);
    PhClearReference(&ProcessNode->StartTimeText);
    PhClearReference(&ProcessNode->TotalCpuTimeText);
    PhClearReference(&ProcessNode->KernelCpuTimeText);
    PhClearReference(&ProcessNode->UserCpuTimeText);
    PhClearReference(&ProcessNode->RelativeStartTimeText);
    PhClearReference(&ProcessNode->WindowTitleText);
    PhClearReference(&ProcessNode->DepStatusText);
    PhClearReference(&ProcessNode->CyclesText);
    PhClearReference(&ProcessNode->CyclesDeltaText);
    PhClearReference(&ProcessNode->ContextSwitchesText);
    PhClearReference(&ProcessNode->ContextSwitchesDeltaText);
    PhClearReference(&ProcessNode->PageFaultsDeltaText);

    for (i = 0; i < PHPRTLC_IOGROUP_COUNT; i++)
        PhClearReference(&ProcessNode->IoGroupText[i]);

    PhClearReference(&ProcessNode->PagedPoolText);
    PhClearReference(&ProcessNode->PeakPagedPoolText);
    PhClearReference(&ProcessNode->NonPagedPoolText);
    PhClearReference(&ProcessNode->PeakNonPagedPoolText);
    PhClearReference(&ProcessNode->MinimumWorkingSetText);
    PhClearReference(&ProcessNode->MaximumWorkingSetText);
    PhClearReference(&ProcessNode->PrivateBytesDeltaText);
    PhClearReference(&ProcessNode->TimeStampText);
    PhClearReference(&ProcessNode->FileModifiedTimeText);
    PhClearReference(&ProcessNode->FileSizeText);
    PhClearReference(&ProcessNode->SubprocessCountText);
    PhClearReference(&ProcessNode->JobObjectIdText);
    PhClearReference(&ProcessNode->DesktopInfoText);
    PhClearReference(&ProcessNode->CpuCoreUsageText);
    PhClearReference(&ProcessNode->ImageCoherencyText);
    PhClearReference(&ProcessNode->ImageCoherencyStatusText);
    PhClearReference(&ProcessNode->ErrorModeText);
    PhClearReference(&ProcessNode->CodePageText);
    PhClearReference(&ProcessNode->ParentPidText);
    PhClearReference(&ProcessNode->ParentConsolePidText);
    PhClearReference(&ProcessNode->SharedCommitText);
    PhClearReference(&ProcessNode->CpuAverageText);
    PhClearReference(&ProcessNode->CpuKernelText);
    PhClearReference(&ProcessNode->CpuUserText);
    PhClearReference(&ProcessNode->GrantedAccessText);
    PhClearReference(&ProcessNode->TlsBitmapDeltaText);
    PhClearReference(&ProcessNode->ReferenceCountText);
    PhClearReference(&ProcessNode->LxssProcessIdText);
    PhClearReference(&ProcessNode->ProcessStartKeyText);
    PhClearReference(&ProcessNode->MitigationPoliciesText);

    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);

    PhClearReference(&ProcessNode->TooltipText);

    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;

    // Header text invalidation (dmex)

    memset(PhProcessTreeColumnHeaderCache, 0, PhProcessTreeColumnHeaderCacheLength);

    // Node 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 | PHPN_APPID | PHPN_DESKTOPINFO | PHPN_CODEPAGE | PHPN_GRANTEDACCESS; // Items that always remain valid

        // The DPI awareness defaults to unaware if not set or declared in the manifest in which case
        // it can be changed once, so we can only be sure that it won't be changed again if it is different
        // from Unaware (poizan42).
        if (node->DpiAwareness != PH_PROCESS_DPI_AWARENESS_UNAWARE + 1)
            node->ValidMask &= ~PHPN_DPIAWARENESS;

        // Invalidate graph buffers.
        node->CpuGraphBuffers.Valid = FALSE;
        node->PrivateGraphBuffers.Valid = FALSE;
        node->IoGraphBuffers.Valid = FALSE;
    }

    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, NULL);

    if (ProcessTreeListHandle && !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
    )
{
    BITMAPINFO bitmapInfo;

    // 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. (wj32)
        SelectBitmap(GraphContext, GraphOldBitmap);
        DeleteBitmap(GraphBitmap);
        DeleteDC(GraphContext);

        GraphContext = NULL;
        GraphBitmap = NULL;
        GraphBits = NULL;
    }

    GraphContextWidth = Width;
    GraphContextHeight = Height;

    memset(&bitmapInfo, 0, sizeof(BITMAPINFO));
    bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bitmapInfo.bmiHeader.biPlanes = 1;
    bitmapInfo.bmiHeader.biCompression = BI_RGB;
    bitmapInfo.bmiHeader.biWidth = Width;
    bitmapInfo.bmiHeader.biHeight = Height;
    bitmapInfo.bmiHeader.biBitCount = 32;

    GraphContext = CreateCompatibleDC(NULL);
    GraphBitmap = CreateDIBSection(GraphContext, &bitmapInfo, DIB_RGB_COLORS, &GraphBits, NULL, 0);
    GraphOldBitmap = SelectBitmap(GraphContext, GraphBitmap);
}

_Success_(return)
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(UNICODE_NULL);
        }

        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 PhpAggregateFieldTotal(
    _In_ PPH_PROCESS_NODE ProcessNode,
    _In_ PHP_AGGREGATE_TYPE Type,
    _In_ PHP_AGGREGATE_LOCATION Location,
    _In_ SIZE_T FieldOffset,
    _Inout_ PVOID AggregatedValue
    )
{
    PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type);
}

static VOID PhpUpdateProcessNodeWsCounters(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_WSCOUNTERS))
    {
        BOOLEAN success = FALSE;

        if (
            PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessItem->ProcessId) &&
            ProcessNode->ProcessItem->IsHandleValid // PROCESS_QUERY_INFORMATION
            )
        {
            if (NT_SUCCESS(PhGetProcessWsCounters(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->WsCounters)))
                success = TRUE;
        }

        if (!success)
            memset(&ProcessNode->WsCounters, 0, sizeof(PH_PROCESS_WS_COUNTERS));

        ProcessNode->ValidMask |= PHPN_WSCOUNTERS;
    }
}

static VOID PhpUpdateProcessNodeGdiUserHandles(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!FlagOn(ProcessNode->ValidMask, PHPN_GDIUSERHANDLES))
    {
        if (ProcessNode->ProcessItem->QueryHandle)
        {
            if (!NT_SUCCESS(PhGetProcessGuiResources(ProcessNode->ProcessItem->QueryHandle, GR_GDIOBJECTS, &ProcessNode->GdiHandles)))
                ProcessNode->GdiHandles = 0;
            if (!NT_SUCCESS(PhGetProcessGuiResources(ProcessNode->ProcessItem->QueryHandle, GR_USEROBJECTS, &ProcessNode->UserHandles)))
                ProcessNode->UserHandles = 0;
        }
        else
        {
            ProcessNode->GdiHandles = 0;
            ProcessNode->UserHandles = 0;
        }

        SetFlag(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 = ULONG_MAX;
            if (!NT_SUCCESS(PhGetProcessPagePriority(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->PagePriority)))
                ProcessNode->PagePriority = ULONG_MAX;
        }
        else
        {
            ProcessNode->IoPriority = ULONG_MAX;
            ProcessNode->PagePriority = ULONG_MAX;
        }

        ProcessNode->ValidMask |= PHPN_IOPAGEPRIORITY;
    }
}

static VOID PhpUpdateProcessNodeWindow(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_WINDOW))
    {
        PhClearReference(&ProcessNode->WindowText);

        if (ProcessNode->ProcessItem->QueryHandle && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            if (!ProcessNode->WindowHandle)
            {
                ProcessNode->WindowHandle = PhGetProcessMainWindow(
                    ProcessNode->ProcessId,
                    ProcessNode->ProcessItem->QueryHandle
                    );
            }

            if (ProcessNode->WindowHandle)
            {
                PhGetWindowTextEx(
                    ProcessNode->WindowHandle,
                    PH_GET_WINDOW_TEXT_INTERNAL,
                    &ProcessNode->WindowText
                    );

                ProcessNode->WindowHung = !!PhIsHungAppWindow(ProcessNode->WindowHandle);
            }
        }

        ProcessNode->ValidMask |= PHPN_WINDOW;
    }
}

static VOID PhpUpdateProcessNodeDepStatus(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!FlagOn(ProcessNode->ValidMask, PHPN_DEPSTATUS))
    {
        ULONG depStatus = 0;

#ifdef _WIN64
        if (
            PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessItem->ProcessId) &&
            ProcessNode->ProcessItem->IsWow64Process &&
            ProcessNode->ProcessItem->IsHandleValid // PROCESS_QUERY_INFORMATION
            )
#else
        if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessItem->ProcessId) && ProcessNode->ProcessItem->QueryHandle)
#endif
        {
            PhGetProcessDepStatus(ProcessNode->ProcessItem->QueryHandle, &depStatus);
        }
        else
        {
            if (ProcessNode->ProcessItem->QueryHandle)
            {
                depStatus = PH_PROCESS_DEP_ENABLED | PH_PROCESS_DEP_PERMANENT;
            }
        }

        ProcessNode->DepStatus = depStatus;

        SetFlag(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 (ProcessNode->ProcessItem->QueryHandle)
        {
            if (NT_SUCCESS(PhOpenProcessToken(
                ProcessNode->ProcessItem->QueryHandle,
                TOKEN_QUERY,
                &tokenHandle
                )))
            {
                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 (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessItem->ProcessId) && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, ProcessNode->ProcessId)))
            {
                if (NT_SUCCESS(PhGetProcessSwitchContext(processHandle, &ProcessNode->OsContextGuid)))
                {
                    if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN10_CONTEXT_GUID))
                        ProcessNode->OsContextVersion = WINDOWS_10;
                    else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WINBLUE_CONTEXT_GUID))
                        ProcessNode->OsContextVersion = WINDOWS_8_1;
                    else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN8_CONTEXT_GUID))
                        ProcessNode->OsContextVersion = WINDOWS_8;
                    else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN7_CONTEXT_GUID))
                        ProcessNode->OsContextVersion = WINDOWS_7;
                    else if (IsEqualGUID(&ProcessNode->OsContextGuid, &VISTA_CONTEXT_GUID))
                        ProcessNode->OsContextVersion = WINDOWS_VISTA;
                    else if (IsEqualGUID(&ProcessNode->OsContextGuid, &XP_CONTEXT_GUID))
                        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(PhGetProcessQuotaLimits(
            ProcessNode->ProcessItem->QueryHandle,
            &quotaLimits
            )))
        {
            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))
    {
        ProcessNode->ImageMachine = IMAGE_FILE_MACHINE_UNKNOWN;

        if (ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            ProcessNode->ImageSubsystem = IMAGE_SUBSYSTEM_POSIX_CUI;
        }
        else if (ProcessNode->ProcessItem->FileName)
        {
            PH_MAPPED_IMAGE mappedImage;

#ifdef _ARM64_
            if (NT_SUCCESS(PhLoadMappedImageEx(&ProcessNode->ProcessItem->FileName->sr, NULL, &mappedImage)))
#else
            if (NT_SUCCESS(PhLoadMappedImageHeaderPageSize(&ProcessNode->ProcessItem->FileName->sr, NULL, &mappedImage)))
#endif
            {
                ProcessNode->ImageMachine = mappedImage.NtHeaders->FileHeader.Machine;
                ProcessNode->ImageTimeDateStamp = mappedImage.NtHeaders->FileHeader.TimeDateStamp;
                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;
                }

#ifdef _ARM64_
                if (ProcessNode->ImageMachine == IMAGE_FILE_MACHINE_AMD64 || ProcessNode->ImageMachine == IMAGE_FILE_MACHINE_ARM64)
                    ProcessNode->ImageCHPEVersion = PhGetMappedImageCHPEVersion(&mappedImage);
#endif

                PhUnloadMappedImage(&mappedImage);
            }
        }

        if (!ProcessNode->ProcessItem->QueryHandle ||
            !NT_SUCCESS(PhGetProcessArchitecture(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->Architecture)))
        {
            ProcessNode->Architecture = IMAGE_FILE_MACHINE_UNKNOWN;
        }

        //if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessItem->ProcessId))
        //{
        //    HANDLE processHandle;
        //    PVOID imageBaseAddress;
        //    PH_REMOTE_MAPPED_IMAGE mappedImage;
        //
        //    if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, ProcessNode->ProcessId)))
        //    {
        //        if (NT_SUCCESS(PhGetProcessImageBaseAddress(processHandle, &imageBaseAddress)))
        //        {
        //            if (NT_SUCCESS(PhLoadRemoteMappedImage(processHandle, imageBaseAddress, &mappedImage)))
        //            {
        //                ProcessNode->ImageTimeDateStamp = mappedImage.NtHeaders->FileHeader.TimeDateStamp;
        //                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))
    {
        ULONG windowFlags;
        PPH_STRING applicationUserModelId;

        PhClearReference(&ProcessNode->AppIdText);

        if (ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            NOTHING;
        }
        else if (HR_SUCCESS(PhAppResolverGetAppIdForProcess(
            ProcessNode->ProcessId,
            &applicationUserModelId
            )))
        {
            ProcessNode->AppIdText = applicationUserModelId;
        }
        else
        {
            if (ProcessNode->ProcessItem->QueryHandle)
            {
                if (NT_SUCCESS(PhGetProcessWindowTitle(
                    ProcessNode->ProcessItem->QueryHandle,
                    &windowFlags,
                    &applicationUserModelId
                    )))
                {
                    if (windowFlags & STARTF_TITLEISAPPID)
                        ProcessNode->AppIdText = applicationUserModelId;
                    else
                        PhDereferenceObject(applicationUserModelId);
                }

                //if (WindowsVersion >= WINDOWS_8 && ProcessNode->ProcessItem->IsImmersive)
                //{
                //    HANDLE tokenHandle;
                //
                //    if (NT_SUCCESS(PhOpenProcessToken(
                //        ProcessNode->ProcessItem->QueryHandle,
                //        TOKEN_QUERY,
                //        &tokenHandle
                //        )))
                //    {
                //        ProcessNode->AppIdText = PhGetTokenPackageApplicationUserModelId(tokenHandle);
                //        NtClose(tokenHandle);
                //    }
                //}
            }
        }

        ProcessNode->ValidMask |= PHPN_APPID;
    }
}

static VOID PhpUpdateProcessNodeDpiAwareness(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_DPIAWARENESS))
    {
        if (ProcessNode->ProcessItem->QueryHandle && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            PH_PROCESS_DPI_AWARENESS dpiAwareness;

            if (PhGetProcessDpiAwareness(ProcessNode->ProcessItem->QueryHandle, &dpiAwareness))
                ProcessNode->DpiAwareness = dpiAwareness + 1;
        }

        ProcessNode->ValidMask |= PHPN_DPIAWARENESS;
    }
}

static VOID PhpUpdateProcessNodeFileAttributes(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_FILEATTRIBUTES))
    {
        FILE_NETWORK_OPEN_INFORMATION networkOpenInfo;

        if (ProcessNode->ProcessItem->FileName && NT_SUCCESS(PhQueryFullAttributesFile(
            &ProcessNode->ProcessItem->FileName->sr,
            &networkOpenInfo
            )))
        {
            ProcessNode->FileLastWriteTime = networkOpenInfo.LastWriteTime;
            ProcessNode->FileEndOfFile = networkOpenInfo.EndOfFile;
        }
        else
        {
            ProcessNode->FileLastWriteTime.QuadPart = 0;
            ProcessNode->FileEndOfFile.QuadPart = 0;
        }

        ProcessNode->ValidMask |= PHPN_FILEATTRIBUTES;
    }
}

static VOID PhpUpdateProcessNodeDesktopInfo(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_DESKTOPINFO))
    {
        HANDLE processHandle;

        PhClearReference(&ProcessNode->DesktopInfoText);

        if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessId) && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            if (NT_SUCCESS(PhOpenProcess(
                &processHandle,
                PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
                ProcessNode->ProcessId
                )))
            {
                PPH_STRING desktopinfo;

                if (NT_SUCCESS(PhGetProcessDesktopInfo(processHandle, &desktopinfo)))
                {
                    ProcessNode->DesktopInfoText = desktopinfo;
                }

                NtClose(processHandle);
            }
        }

        ProcessNode->ValidMask |= PHPN_DESKTOPINFO;
    }
}

static VOID PhpUpdateProcessBreakOnTermination(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_CRITICAL))
    {
        ProcessNode->BreakOnTerminationEnabled = FALSE;

        if (ProcessNode->ProcessItem->QueryHandle && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            BOOLEAN breakOnTermination;

            if (NT_SUCCESS(PhGetProcessBreakOnTermination(
                ProcessNode->ProcessItem->QueryHandle,
                &breakOnTermination
                )))
            {
                ProcessNode->BreakOnTerminationEnabled = breakOnTermination;
            }
        }

        ProcessNode->ValidMask |= PHPN_CRITICAL;
    }
}

static VOID PhpUpdateProcessNodeErrorMode(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_ERRORMODE))
    {
        PhClearReference(&ProcessNode->ErrorModeText);

        if (ProcessNode->ProcessItem->QueryHandle && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            ULONG errorMode;

            if (NT_SUCCESS(PhGetProcessErrorMode(
                ProcessNode->ProcessItem->QueryHandle,
                &errorMode
                )) && errorMode > 0)
            {
                PH_STRING_BUILDER stringBuilder;
                WCHAR pointer[PH_PTR_STR_LEN_1];

                PhInitializeStringBuilder(&stringBuilder, 0x50);

                if (errorMode & SEM_FAILCRITICALERRORS)
                {
                    PhAppendStringBuilder2(&stringBuilder, L"Fail critical, ");
                }

                if (errorMode & SEM_NOGPFAULTERRORBOX)
                {
                    PhAppendStringBuilder2(&stringBuilder, L"GP faults, ");
                }

                if (errorMode & SEM_NOALIGNMENTFAULTEXCEPT)
                {
                    PhAppendStringBuilder2(&stringBuilder, L"Alignment faults, ");
                }

                if (errorMode & SEM_NOOPENFILEERRORBOX)
                {
                    PhAppendStringBuilder2(&stringBuilder, L"Openfile faults, ");
                }

                if (PhEndsWithString2(stringBuilder.String, L", ", FALSE))
                    PhRemoveEndStringBuilder(&stringBuilder, 2);

                PhPrintPointer(pointer, UlongToPtr(errorMode));
                PhAppendFormatStringBuilder(&stringBuilder, L" (%s)", pointer);

                ProcessNode->ErrorModeText = PhFinalStringBuilderString(&stringBuilder);
            }
        }

        ProcessNode->ValidMask |= PHPN_ERRORMODE;
    }
}

static VOID PhpUpdateProcessNodeCodePage(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_CODEPAGE))
    {
        if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessId) && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            HANDLE processHandle;

            if (NT_SUCCESS(PhOpenProcess(
                &processHandle,
                PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
                ProcessNode->ProcessId
                )))
            {
                USHORT codePage;

                if (NT_SUCCESS(PhGetProcessCodePage(processHandle, &codePage)))
                {
                    ProcessNode->CodePage = codePage;
                }

                NtClose(processHandle);
            }
        }

        ProcessNode->ValidMask |= PHPN_CODEPAGE;
    }
}

static VOID PhpUpdateProcessNodePowerThrottling(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!(ProcessNode->ValidMask & PHPN_POWERTHROTTLING))
    {
        ProcessNode->PowerThrottling = FALSE;

        if (ProcessNode->ProcessItem->QueryHandle && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            POWER_THROTTLING_PROCESS_STATE powerThrottlingState;

            if (NT_SUCCESS(PhGetProcessPowerThrottlingState(ProcessNode->ProcessItem->QueryHandle, &powerThrottlingState)))
            {
                if (powerThrottlingState.ControlMask & POWER_THROTTLING_PROCESS_EXECUTION_SPEED &&
                    powerThrottlingState.StateMask & POWER_THROTTLING_PROCESS_EXECUTION_SPEED)
                {
                    ProcessNode->PowerThrottling = TRUE;
                }
            }
        }

        ProcessNode->ValidMask |= PHPN_POWERTHROTTLING;
    }
}

static VOID PhpUpdateProcessNodePriorityBoost(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!FlagOn(ProcessNode->ValidMask, PHPN_PRIORITYBOOST))
    {
        ProcessNode->PriorityBoost = FALSE;

        if (ProcessNode->ProcessItem->QueryHandle && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            BOOLEAN priorityBoostDisabled;

            if (NT_SUCCESS(PhGetProcessPriorityBoost(ProcessNode->ProcessItem->QueryHandle, &priorityBoostDisabled)))
            {
                ProcessNode->PriorityBoost = !priorityBoostDisabled;
            }
        }

        SetFlag(ProcessNode->ValidMask, PHPN_PRIORITYBOOST);
    }
}

static VOID PhpUpdateProcessNodeGrantedAccess(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!FlagOn(ProcessNode->ValidMask, PHPN_GRANTEDACCESS))
    {
        PhClearReference(&ProcessNode->GrantedAccessText);

        if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessId))
        {
            NTSTATUS status;
            HANDLE processHandle;
            ACCESS_MASK processAccess;

            if (ProcessNode->ProcessItem->IsProtectedProcess && ProcessNode->ProcessItem->Protection.Type == PsProtectedTypeProtectedLight)
                processAccess = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_LIMITED_INFORMATION;
            else if (ProcessNode->ProcessItem->IsProtectedProcess && ProcessNode->ProcessItem->Protection.Type == PsProtectedTypeProtected)
                processAccess = PROCESS_QUERY_LIMITED_INFORMATION;
            else if (ProcessNode->ProcessItem->IsSubsystemProcess && KsiLevel() != KphLevelMax)
                processAccess = PROCESS_QUERY_LIMITED_INFORMATION;
            else
                processAccess = MAXIMUM_ALLOWED;

            status = PhOpenProcess(
                &processHandle,
                processAccess,
                ProcessNode->ProcessId
                );

            if (NT_SUCCESS(status))
            {
                OBJECT_BASIC_INFORMATION basicInfo;

                status = PhQueryObjectBasicInformation(processHandle, &basicInfo);

                if (NT_SUCCESS(status))
                {
                    PPH_ACCESS_ENTRY accessEntries;
                    ULONG numberOfAccessEntries;

                    if (PhGetAccessEntries(L"Process", &accessEntries, &numberOfAccessEntries))
                    {
                        ProcessNode->GrantedAccessText = PhGetAccessString(basicInfo.GrantedAccess, accessEntries, numberOfAccessEntries);
                        PhFree(accessEntries);
                    }

                    if (ProcessNode->GrantedAccessText)
                    {
                        WCHAR grantedAccessString[PH_PTR_STR_LEN_1];
                        PhPrintPointer(grantedAccessString, UlongToPtr(basicInfo.GrantedAccess));
                        PhMoveReference(&ProcessNode->GrantedAccessText, PhFormatString(
                            L"%s (%s)",
                            PhGetString(ProcessNode->GrantedAccessText),
                            grantedAccessString
                            ));
                    }
                }

                NtClose(processHandle);
            }

            if (!NT_SUCCESS(status))
            {
                PhMoveReference(&ProcessNode->GrantedAccessText, PhGetStatusMessage(status, 0));
            }
        }

        SetFlag(ProcessNode->ValidMask, PHPN_GRANTEDACCESS);
    }
}

static VOID PhpUpdateProcessNodeTlsBitmapDelta(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!FlagOn(ProcessNode->ValidMask, PHPN_TLSBITMAPDELTA))
    {
        if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessId) && !ProcessNode->ProcessItem->IsSubsystemProcess)
        {
            HANDLE processHandle;

            if (NT_SUCCESS(PhOpenProcess(
                &processHandle,
                PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
                ProcessNode->ProcessId
                )))
            {
                ULONG bitmapCount;
                ULONG bitmapExpansionCount;

                if (NT_SUCCESS(PhGetProcessTlsBitMapCounters(processHandle, &bitmapCount, &bitmapExpansionCount)))
                {
                    ProcessNode->TlsBitmapCount = (USHORT)(bitmapCount + bitmapExpansionCount);
                }

                NtClose(processHandle);
            }
        }

        SetFlag(ProcessNode->ValidMask, PHPN_TLSBITMAPDELTA);
    }
}

static VOID PhpUpdateProcessNodeObjectReferences(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!FlagOn(ProcessNode->ValidMask, PHPN_REFERENCEDELTA))
    {
        if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessId))
        {
            ProcessNode->ReferenceCount = 0;

            if (ProcessNode->ProcessItem->QueryHandle)
            {
                OBJECT_BASIC_INFORMATION basicInfo;

                if (NT_SUCCESS(PhQueryObjectBasicInformation(ProcessNode->ProcessItem->QueryHandle, &basicInfo)))
                {
                    ProcessNode->ReferenceCount = basicInfo.HandleCount;
                }
            }
        }

        SetFlag(ProcessNode->ValidMask, PHPN_REFERENCEDELTA);
    }
}

static VOID PhpUpdateProcessNodeStartKey(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!FlagOn(ProcessNode->ValidMask, PHPN_STARTKEY))
    {
        if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessId))
        {
            ProcessNode->ProcessStartKey = 0;

            if (ProcessNode->ProcessItem->QueryHandle)
            {
                ULONGLONG processStartKey;

                if (NT_SUCCESS(PhGetProcessStartKey(ProcessNode->ProcessItem->QueryHandle, &processStartKey)))
                {
                    PH_FORMAT format[2];

                    PhInitFormatS(&format[0], L"0x");
                    PhInitFormatI64X(&format[1], processStartKey);
                    PhMoveReference(&ProcessNode->ProcessStartKeyText, PhFormat(format, 2, 0));
                    ProcessNode->ProcessStartKey = processStartKey;
                }
            }
        }

        SetFlag(ProcessNode->ValidMask, PHPN_STARTKEY);
    }
}

static VOID PhpUpdateProcessNodeMitigationPolicies(
    _Inout_ PPH_PROCESS_NODE ProcessNode
    )
{
    if (!FlagOn(ProcessNode->ValidMask, PHPN_MITIGATIONPOLICIES))
    {
        NTSTATUS status;
        PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION information;

        if (ProcessNode->ProcessItem->QueryHandle &&
            NT_SUCCESS(status = PhGetProcessMitigationPolicy(ProcessNode->ProcessItem->QueryHandle, &information)))
        {
            PH_STRING_BUILDER sb;
            PROCESS_MITIGATION_POLICY policy;
            PPH_STRING shortDescription;

            PhInitializeStringBuilder(&sb, 100);

            for (policy = 0; policy < MaxProcessMitigationPolicy; policy++)
            {
                if (information.Pointers[policy] && PhDescribeProcessMitigationPolicy(
                    policy,
                    information.Pointers[policy],
                    &shortDescription,
                    NULL
                    ))
                {
                    PhAppendStringBuilder(&sb, &shortDescription->sr);
                    PhAppendStringBuilder2(&sb, L"; ");
                    PhDereferenceObject(shortDescription);
                }
            }

            // HACK: Show System process CET mitigation (dmex)
            if (ProcessNode->ProcessItem->ProcessId == SYSTEM_PROCESS_ID)
            {
                SYSTEM_SHADOW_STACK_INFORMATION shadowStackInformation;

                if (NT_SUCCESS(PhGetSystemShadowStackInformation(&shadowStackInformation)))
                {
                    PROCESS_MITIGATION_USER_SHADOW_STACK_POLICY policyData;

                    memset(&policyData, 0, sizeof(PROCESS_MITIGATION_USER_SHADOW_STACK_POLICY));
                    policyData.EnableUserShadowStack = shadowStackInformation.KernelCetEnabled;
                    policyData.EnableUserShadowStackStrictMode = shadowStackInformation.KernelCetEnabled;
                    policyData.AuditUserShadowStack = shadowStackInformation.KernelCetAuditModeEnabled;

                    if (PhDescribeProcessMitigationPolicy(
                        ProcessUserShadowStackPolicy,
                        &policyData,
                        &shortDescription,
                        NULL
                        ))
                    {
                        PhAppendStringBuilder(&sb, &shortDescription->sr);
                        PhAppendStringBuilder2(&sb, L"; ");
                        PhDereferenceObject(shortDescription);
                    }
                }
            }

            if (sb.String->Length != 0)
                PhRemoveEndStringBuilder(&sb, 2);

            ProcessNode->MitigationPoliciesText = PhFinalStringBuilderString(&sb);
        }

        SetFlag(ProcessNode->ValidMask, PHPN_MITIGATIONPOLICIES);
    }
}

#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 = PhCompareStringWithNullSortOrder(
        processItem1->ProcessName,
        processItem2->ProcessName,
        ProcessTreeListSortOrder,
        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 = PhCompareStringWithNullSortOrder(
        processItem1->UserName,
        processItem2->UserName,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Description)
{
    sortResult = PhCompareStringWithNullSortOrder(
        processItem1->VersionInfo.FileDescription,
        processItem2->VersionInfo.FileDescription,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(CompanyName)
{
    sortResult = PhCompareStringWithNullSortOrder(
        processItem1->VersionInfo.CompanyName,
        processItem2->VersionInfo.CompanyName,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Version)
{
    sortResult = PhCompareStringWithNullSortOrder(
        processItem1->VersionInfo.FileVersion,
        processItem2->VersionInfo.FileVersion,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(FileName)
{
    sortResult = PhCompareStringWithNullSortOrder(
        processItem1->FileName,
        processItem2->FileName,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(CommandLine)
{
    sortResult = PhCompareStringWithNullSortOrder(
        processItem1->CommandLine,
        processItem2->CommandLine,
        ProcessTreeListSortOrder,
        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)
{
    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)
{
    PhpUpdateProcessNodeGdiUserHandles(node1);
    PhpUpdateProcessNodeGdiUserHandles(node2);

    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.Level, processItem2->IntegrityLevel.Level);
}
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 = uintcmp(processItem1->VerifyResult, processItem2->VerifyResult);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(VerifiedSigner)
{
    sortResult = PhCompareStringWithNullSortOrder(
        processItem1->VerifySignerName,
        processItem2->VerifySignerName,
        ProcessTreeListSortOrder,
        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 = uintcmp(processItem1->IsWow64Process, processItem2->IsWow64Process);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Elevation)
{
    ULONG key1 = (ULONG)processItem1->ElevationType + (processItem1->IsElevated ? 4 : 0);
    ULONG key2 = (ULONG)processItem2->ElevationType + (processItem2->IsElevated ? 4 : 0);

    sortResult = uintcmp(key1, key2);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(WindowTitle)
{
    PhpUpdateProcessNodeWindow(node1);
    PhpUpdateProcessNodeWindow(node2);
    sortResult = PhCompareStringWithNullSortOrder(
        node1->WindowText,
        node2->WindowText,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(WindowStatus)
{
    PhpUpdateProcessNodeWindow(node1);
    PhpUpdateProcessNodeWindow(node2);
    sortResult = ucharcmp(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(processItem1->CycleTimeDelta.Value, processItem2->CycleTimeDelta.Value);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(CyclesDelta)
{
    sortResult = uint64cmp(processItem1->CycleTimeDelta.Delta, processItem2->CycleTimeDelta.Delta);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Dep)
{
    PhpUpdateProcessNodeDepStatus(node1);
    PhpUpdateProcessNodeDepStatus(node2);
    sortResult = uintcmp(node1->DepStatus, node2->DepStatus);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Virtualized)
{
    PhpUpdateProcessNodeToken(node1);
    PhpUpdateProcessNodeToken(node2);
    sortResult = ucharcmp(node1->VirtualizationEnabled, node2->VirtualizationEnabled);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(ContextSwitches)
{
    sortResult = uint64cmp(processItem1->ContextSwitchesDelta.Value, processItem2->ContextSwitchesDelta.Value);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(ContextSwitchesDelta)
{
    sortResult = int64cmp((LONGLONG)processItem1->ContextSwitchesDelta.Delta, (LONGLONG)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)
{
    LONG_PTR value1 = processItem1->PrivateBytesDelta.Delta;
    LONG_PTR value2 = processItem2->PrivateBytesDelta.Delta;

    // Ignore zero when sorting (dmex)
    if (value1 != 0 && value2 != 0)
    {
        if (value1 > value2)
            return -1;
        else if (value1 < value2)
            return 1;

        return 0;
    }
    else if (value1 == 0)
    {
        return value2 == 0 ? 0 : (ProcessTreeListSortOrder == AscendingSortOrder ? -1 : 1);
    }
    else
    {
        return (ProcessTreeListSortOrder == AscendingSortOrder ? 1 : -1);
    }
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Subsystem)
{
    PhpUpdateProcessNodeImage(node1);
    PhpUpdateProcessNodeImage(node2);
    sortResult = ushortcmp(node1->ImageSubsystem, node2->ImageSubsystem);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(PackageName)
{
    sortResult = PhCompareStringWithNullSortOrder(
        processItem1->PackageFullName,
        processItem2->PackageFullName,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(AppId)
{
    PhpUpdateProcessNodeAppId(node1);
    PhpUpdateProcessNodeAppId(node2);
    sortResult = PhCompareStringWithNullSortOrder(
        node1->AppIdText,
        node2->AppIdText,
        ProcessTreeListSortOrder,
        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)
{
    // prefer XFG over CFG
    sortResult = uintcmp(node1->ProcessItem->IsXfgEnabled, node2->ProcessItem->IsXfgEnabled);

    if (sortResult == 0)
    {
        // prefer XFG audit over CFG
        sortResult = uintcmp(node1->ProcessItem->IsXfgAuditEnabled, node2->ProcessItem->IsXfgAuditEnabled);

        if (sortResult == 0)
        {
            sortResult = uintcmp(node1->ProcessItem->IsControlFlowGuardEnabled, node2->ProcessItem->IsControlFlowGuardEnabled);
        }
    }
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(TimeStamp)
{
    PhpUpdateProcessNodeImage(node1);
    PhpUpdateProcessNodeImage(node2);
    sortResult = uintcmp(node1->ImageTimeDateStamp, node2->ImageTimeDateStamp);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(FileModifiedTime)
{
    PhpUpdateProcessNodeFileAttributes(node1);
    PhpUpdateProcessNodeFileAttributes(node2);
    sortResult = int64cmp(node1->FileLastWriteTime.QuadPart, node2->FileLastWriteTime.QuadPart);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(FileSize)
{
    PhpUpdateProcessNodeFileAttributes(node1);
    PhpUpdateProcessNodeFileAttributes(node2);
    sortResult = int64cmp(node1->FileEndOfFile.QuadPart, node2->FileEndOfFile.QuadPart);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Subprocesses)
{
    sortResult = uint64cmp(node1->Children->Count, node2->Children->Count);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(JobObjectId)
{
    sortResult = uint64cmp(processItem1->JobObjectId, processItem2->JobObjectId);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Protection)
{
    sortResult = ucharcmp((BOOLEAN)processItem1->IsSecureProcess, (BOOLEAN)processItem2->IsSecureProcess);
    if (sortResult == 0)
        sortResult = ucharcmp((BOOLEAN)processItem1->IsProtectedProcess, (BOOLEAN)processItem2->IsProtectedProcess);
        if (sortResult == 0)
            sortResult = ucharcmp(processItem1->Protection.Level, processItem2->Protection.Level);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(DesktopInfo)
{
    PhpUpdateProcessNodeDesktopInfo(node1);
    PhpUpdateProcessNodeDesktopInfo(node2);
    sortResult = PhCompareStringWithNullSortOrder(
        node1->DesktopInfoText,
        node2->DesktopInfoText,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Critical)
{
    PhpUpdateProcessBreakOnTermination(node1);
    PhpUpdateProcessBreakOnTermination(node2);
    sortResult = ucharcmp(node1->BreakOnTerminationEnabled, node2->BreakOnTerminationEnabled);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(HexPid)
{
    sortResult = intptrcmp((LONG_PTR)processItem1->ProcessId, (LONG_PTR)processItem2->ProcessId);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(CpuCore)
{
    sortResult = singlecmp(processItem1->CpuUsage, processItem2->CpuUsage);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Cet)
{
    sortResult = uintcmp(node1->ProcessItem->IsCetEnabled, node2->ProcessItem->IsCetEnabled);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(ImageCoherency)
{
    sortResult = singlecmp(processItem1->ImageCoherency, processItem2->ImageCoherency);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(Architecture)
{
    USHORT architecture1;
    USHORT architecture2;

    PhpUpdateProcessNodeImage(node1);
    PhpUpdateProcessNodeImage(node2);

    if (node1->Architecture != IMAGE_FILE_MACHINE_UNKNOWN)
        architecture1 = node1->Architecture;
    else
        architecture1 = node1->ImageMachine;

    if (node2->Architecture != IMAGE_FILE_MACHINE_UNKNOWN)
        architecture2 = node2->Architecture;
    else
        architecture2 = node2->ImageMachine;

    sortResult = ushortcmp(architecture1, architecture2);
#ifdef _ARM64_
    if (sortResult == 0)
        sortResult = uintcmp(node1->ImageCHPEVersion, node2->ImageCHPEVersion);
#endif
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(ErrorMode)
{
    PhpUpdateProcessNodeErrorMode(node1);
    PhpUpdateProcessNodeErrorMode(node2);
    sortResult = PhCompareStringWithNullSortOrder(
        node1->ErrorModeText,
        node2->ErrorModeText,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(CodePage)
{
    PhpUpdateProcessNodeCodePage(node1);
    PhpUpdateProcessNodeCodePage(node2);

    sortResult = ushortcmp(node1->CodePage, node2->CodePage);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(PowerThrottling)
{
    PhpUpdateProcessNodePowerThrottling(node1);
    PhpUpdateProcessNodePowerThrottling(node2);
    sortResult = ucharcmp(node1->PowerThrottling, node2->PowerThrottling);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(ParentPid)
{
    sortResult = intptrcmp((LONG_PTR)processItem1->ParentProcessId, (LONG_PTR)processItem2->ParentProcessId);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(ParentConsolePid)
{
    sortResult = intptrcmp((LONG_PTR)processItem1->ConsoleHostProcessId, (LONG_PTR)processItem2->ConsoleHostProcessId);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(SharedCommit)
{
    sortResult = uint64cmp(processItem1->SharedCommitCharge, processItem2->SharedCommitCharge);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(PriorityBoost)
{
    PhpUpdateProcessNodePriorityBoost(node1);
    PhpUpdateProcessNodePriorityBoost(node2);
    sortResult = ucharcmp(node1->PriorityBoost, node2->PriorityBoost);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(CpuAverage)
{
    sortResult = singlecmp(processItem1->CpuAverageUsage, processItem2->CpuAverageUsage);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(CpuKernel)
{
    sortResult = singlecmp(processItem1->CpuKernelUsage, processItem2->CpuKernelUsage);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(CpuUser)
{
    sortResult = singlecmp(processItem1->CpuUserUsage, processItem2->CpuUserUsage);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(GrantedAccess)
{
    PhpUpdateProcessNodeGrantedAccess(node1);
    PhpUpdateProcessNodeGrantedAccess(node2);
    sortResult = PhCompareStringWithNullSortOrder(
        node1->GrantedAccessText,
        node2->GrantedAccessText,
        ProcessTreeListSortOrder,
        TRUE
        );
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(TlsBitmapDelta)
{
    PhpUpdateProcessNodeTlsBitmapDelta(node1);
    PhpUpdateProcessNodeTlsBitmapDelta(node2);
    sortResult = ushortcmp(node1->TlsBitmapCount, node2->TlsBitmapCount);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(ReferenceDelta)
{
    PhpUpdateProcessNodeObjectReferences(node1);
    PhpUpdateProcessNodeObjectReferences(node2);
    sortResult = uintcmp(node1->ReferenceCount, node2->ReferenceCount);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(LxssPid)
{
    sortResult = uintcmp(node1->ProcessItem->LxssProcessId, node2->ProcessItem->LxssProcessId);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(StartKey)
{
    PhpUpdateProcessNodeStartKey(node1);
    PhpUpdateProcessNodeStartKey(node2);
    sortResult = uint64cmp(node1->ProcessStartKey, node2->ProcessStartKey);
}
END_SORT_FUNCTION

BEGIN_SORT_FUNCTION(MitigationPolicies)
{
    PhpUpdateProcessNodeMitigationPolicies(node1);
    PhpUpdateProcessNodeMitigationPolicies(node2);
    sortResult = PhCompareStringWithNullSortOrder(
        node1->MitigationPoliciesText,
        node2->MitigationPoliciesText,
        ProcessTreeListSortOrder,
        TRUE
        );
}
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(Dep),
                        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),
                        SORT_FUNCTION(TimeStamp),
                        SORT_FUNCTION(FileModifiedTime),
                        SORT_FUNCTION(FileSize),
                        SORT_FUNCTION(Subprocesses),
                        SORT_FUNCTION(JobObjectId),
                        SORT_FUNCTION(Protection),
                        SORT_FUNCTION(DesktopInfo),
                        SORT_FUNCTION(Critical),
                        SORT_FUNCTION(HexPid),
                        SORT_FUNCTION(CpuCore),
                        SORT_FUNCTION(Cet),
                        SORT_FUNCTION(ImageCoherency),
                        SORT_FUNCTION(ErrorMode),
                        SORT_FUNCTION(CodePage),
                        SORT_FUNCTION(StartTime), // Timeline
                        SORT_FUNCTION(PowerThrottling),
                        SORT_FUNCTION(Architecture),
                        SORT_FUNCTION(ParentPid),
                        SORT_FUNCTION(ParentConsolePid),
                        SORT_FUNCTION(SharedCommit),
                        SORT_FUNCTION(PriorityBoost),
                        SORT_FUNCTION(CpuAverage),
                        SORT_FUNCTION(CpuKernel),
                        SORT_FUNCTION(CpuUser),
                        SORT_FUNCTION(GrantedAccess),
                        SORT_FUNCTION(TlsBitmapDelta),
                        SORT_FUNCTION(ReferenceDelta),
                        SORT_FUNCTION(LxssPid),
                        SORT_FUNCTION(StartKey),
                        SORT_FUNCTION(MitigationPolicies),
                    };
                    int (__cdecl *sortFunction)(const void *, const void *);

                    static_assert(RTL_NUMBER_OF(sortFunctions) == PHPRTLC_MAXIMUM, "SortFunctions must equal maximum.");

                    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 = PhGetStringRef(processItem->ProcessName);
                break;
            case PHPRTLC_PID:
                {
                    if (PH_IS_REAL_PROCESS_ID(processItem->ProcessId))
                    {
                        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 >= PhMaxPrecisionLimit)
                    {
                        PH_FORMAT format;
                        SIZE_T returnLength;

                        PhInitFormatF(&format, cpuUsage, PhMaxPrecisionUnit);

                        if (PhFormatToBuffer(&format, 1, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength))
                        {
                            getCellText->Text.Buffer = node->CpuUsageText;
                            getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); // minus null terminator
                        }
                        else
                        {
                            PhInitializeEmptyStringRef(&getCellText->Text);
                        }
                    }
                    else if (cpuUsage != 0 && PhCsShowCpuBelow001)
                    {
                        PH_FORMAT format[2];
                        SIZE_T returnLength;

                        PhInitFormatS(&format[0], L"< ");
                        PhInitFormatF(&format[1], cpuUsage, PhMaxPrecisionUnit);

                        if (PhFormatToBuffer(format, 2, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength))
                        {
                            getCellText->Text.Buffer = node->CpuUsageText;
                            getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL);
                        }
                        else
                        {
                            PhInitializeEmptyStringRef(&getCellText->Text);
                        }
                    }
                }
                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->IoWRateText, PhFormatSize(value, ULONG_MAX));
                    getCellText->Text = node->IoWRateText->sr;
                }
                break;
            case PHPRTLC_USERNAME:
                getCellText->Text = PhGetStringRef(processItem->UserName);
                break;
            case PHPRTLC_DESCRIPTION:
                getCellText->Text = PhGetStringRef(processItem->VersionInfo.FileDescription);
                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->FileNameWin32);
                break;
            case PHPRTLC_COMMANDLINE:
                getCellText->Text = PhGetStringRef(processItem->CommandLine);
                break;
            case PHPRTLC_PEAKPRIVATEBYTES:
                {
                    PhMoveReference(&node->PeakPrivateBytesText, PhFormatSize(processItem->VmCounters.PeakPagefileUsage, ULONG_MAX));
                    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, ULONG_MAX));
                    getCellText->Text = node->WorkingSetText->sr;
                }
                break;
            case PHPRTLC_PEAKWORKINGSET:
                {
                    PhMoveReference(&node->PeakWorkingSetText, PhFormatSize(processItem->VmCounters.PeakWorkingSetSize, ULONG_MAX));
                    getCellText->Text = node->PeakWorkingSetText->sr;
                }
                break;
            case PHPRTLC_PRIVATEWS:
                {
                    SIZE_T value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, WorkingSetPrivateSize), &value);
                    PhMoveReference(&node->PrivateWsText, PhFormatSize(value, ULONG_MAX));
                    getCellText->Text = node->PrivateWsText->sr;
                }
                break;
            case PHPRTLC_SHAREDWS:
                {
                    PhpUpdateProcessNodeWsCounters(node);
                    PhMoveReference(&node->SharedWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfSharedPages * PAGE_SIZE, ULONG_MAX));
                    getCellText->Text = node->SharedWsText->sr;
                }
                break;
            case PHPRTLC_SHAREABLEWS:
                {
                    PhpUpdateProcessNodeWsCounters(node);
                    PhMoveReference(&node->ShareableWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfShareablePages * PAGE_SIZE, ULONG_MAX));
                    getCellText->Text = node->ShareableWsText->sr;
                }
                break;
            case PHPRTLC_VIRTUALSIZE:
                {
                    SIZE_T value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.VirtualSize), &value);
                    PhMoveReference(&node->VirtualSizeText, PhFormatSize(value, ULONG_MAX));
                    getCellText->Text = node->VirtualSizeText->sr;
                }
                break;
            case PHPRTLC_PEAKVIRTUALSIZE:
                {
                    PhMoveReference(&node->PeakVirtualSizeText, PhFormatSize(processItem->VmCounters.PeakVirtualSize, ULONG_MAX));
                    getCellText->Text = node->PeakVirtualSizeText->sr;
                }
                break;
            case PHPRTLC_PAGEFAULTS:
                {
                    ULONG value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PageFaultCount), &value);
                    PhMoveReference(&node->PageFaultsText, PhFormatUInt64(value, TRUE));
                    getCellText->Text = node->PageFaultsText->sr;
                }
                break;
            case PHPRTLC_SESSIONID:
                {
                    PhMoveReference(&node->SessionIdText, PhFormatUInt64(processItem->SessionId, TRUE));
                    getCellText->Text = node->SessionIdText->sr;
                }
                break;
            case PHPRTLC_PRIORITYCLASS:
                {
                    if (processItem->PriorityClass != PROCESS_PRIORITY_CLASS_UNKNOWN)
                    {
                        PPH_STRINGREF string;

                        if (string = PhGetProcessPriorityClassString(processItem->PriorityClass))
                        {
                            getCellText->Text.Length = string->Length;
                            getCellText->Text.Buffer = string->Buffer;
                        }
                    }
                }
                break;
            case PHPRTLC_BASEPRIORITY:
                {
                    PhMoveReference(&node->BasePriorityText, PhFormatUInt64(processItem->BasePriority, TRUE));
                    getCellText->Text = node->BasePriorityText->sr;
                }
                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);
                    PhMoveReference(&node->ThreadsText, PhFormatUInt64(value, TRUE));
                    getCellText->Text = node->ThreadsText->sr;
                }
                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);
                    PhMoveReference(&node->HandlesText, PhFormatUInt64(value, TRUE));
                    getCellText->Text = node->HandlesText->sr;
                }
                break;
            case PHPRTLC_GDIHANDLES:
                {
                    PhpUpdateProcessNodeGdiUserHandles(node);

                    ULONG value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, GdiHandles), &value);
                    //PhpFormatInt32GroupDigits(value, node->GdiHandlesText, sizeof(node->GdiHandlesText), &getCellText->Text);
                    PhMoveReference(&node->GdiHandlesText, PhFormatUInt64(value, TRUE));
                    getCellText->Text = node->GdiHandlesText->sr;
                }
                break;
            case PHPRTLC_USERHANDLES:
                {
                    PhpUpdateProcessNodeGdiUserHandles(node);

                    ULONG value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, UserHandles), &value);
                    //PhpFormatInt32GroupDigits(value, node->UserHandlesText, sizeof(node->UserHandlesText), &getCellText->Text);
                    PhMoveReference(&node->UserHandlesText, PhFormatUInt64(value, TRUE));
                    getCellText->Text = node->UserHandlesText->sr;
                }
                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)
                    {
                        getCellText->Text.Buffer = processItem->IntegrityString->Buffer;
                        getCellText->Text.Length = processItem->IntegrityString->Length;
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                break;
            case PHPRTLC_IOPRIORITY:
                {
                    PhpUpdateProcessNodeIoPagePriority(node);

                    if (node->IoPriority != ULONG_MAX && node->IoPriority >= IoPriorityVeryLow && node->IoPriority < MaxIoPriorityTypes)
                    {
                        const PH_STRINGREF ioPriority = PhIoPriorityHintNames[node->IoPriority];

                        getCellText->Text.Buffer = ioPriority.Buffer;
                        getCellText->Text.Length = ioPriority.Length;
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                break;
            case PHPRTLC_PAGEPRIORITY:
                {
                    PhpUpdateProcessNodeIoPagePriority(node);

                    if (node->PagePriority != ULONG_MAX && node->PagePriority <= MEMORY_PRIORITY_NORMAL)
                    {
                        const PH_STRINGREF pagePriority = PhPagePriorityNames[node->PagePriority];

                        getCellText->Text.Buffer = pagePriority.Buffer;
                        getCellText->Text.Length = pagePriority.Length;
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                break;
            case PHPRTLC_STARTTIME:
                {
                    if (processItem->CreateTime.QuadPart != 0)
                    {
                        SYSTEMTIME systemTime;

                        PhLargeIntegerToLocalSystemTime(&systemTime, &processItem->CreateTime);
                        PhMoveReference(&node->StartTimeText, PhFormatDateTime(&systemTime));
                        getCellText->Text = node->StartTimeText->sr;
                    }
                }
                break;
            case PHPRTLC_TOTALCPUTIME:
                {
                    SIZE_T returnLength;
                    WCHAR totalCpuTimeText[PH_TIMESPAN_STR_LEN_1];

                    if (PhPrintTimeSpanToBuffer(
                        processItem->KernelTime.QuadPart + processItem->UserTime.QuadPart,
                        PH_TIMESPAN_DHMSM,
                        totalCpuTimeText,
                        sizeof(totalCpuTimeText),
                        &returnLength
                        ))
                    {
                        PH_STRINGREF string =
                        {
                            .Buffer = totalCpuTimeText,
                            .Length = returnLength - sizeof(UNICODE_NULL)
                        };

                        PhMoveReference(&node->TotalCpuTimeText, PhCreateString2(&string));
                        getCellText->Text = node->TotalCpuTimeText->sr;
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                break;
            case PHPRTLC_KERNELCPUTIME:
                {
                    SIZE_T returnLength;
                    WCHAR kernelCpuTimeText[PH_TIMESPAN_STR_LEN_1];

                    if (PhPrintTimeSpanToBuffer(
                        processItem->KernelTime.QuadPart,
                        PH_TIMESPAN_DHMSM,
                        kernelCpuTimeText,
                        sizeof(kernelCpuTimeText),
                        &returnLength
                        ))
                    {
                        PH_STRINGREF string =
                        {
                            .Buffer = kernelCpuTimeText,
                            .Length = returnLength - sizeof(UNICODE_NULL)
                        };

                        PhMoveReference(&node->KernelCpuTimeText, PhCreateString2(&string));
                        getCellText->Text = node->KernelCpuTimeText->sr;
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                break;
            case PHPRTLC_USERCPUTIME:
                {
                    SIZE_T returnLength;
                    WCHAR userCpuTimeText[PH_TIMESPAN_STR_LEN_1];

                    if (PhPrintTimeSpanToBuffer(
                        processItem->UserTime.QuadPart,
                        PH_TIMESPAN_DHMSM,
                        userCpuTimeText,
                        sizeof(userCpuTimeText),
                        &returnLength
                        ))
                    {
                        PH_STRINGREF string =
                        {
                            .Buffer = userCpuTimeText,
                            .Length = returnLength - sizeof(UNICODE_NULL)
                        };

                        PhMoveReference(&node->UserCpuTimeText, PhCreateString2(&string));
                        getCellText->Text = node->UserCpuTimeText->sr;
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                break;
            case PHPRTLC_VERIFICATIONSTATUS:
                {
                    if (PhEnableProcessQueryStage2)
                        getCellText->Text = PhVerifyResultToStringRef(processItem->VerifyResult);
                    else
                        PhInitializeStringRef(&getCellText->Text, L"Image digital signature support disabled.");
                }
                break;
            case PHPRTLC_VERIFIEDSIGNER:
                {
                    if (PhEnableProcessQueryStage2)
                        getCellText->Text = PhGetStringRef(processItem->VerifySignerName);
                    else
                        PhInitializeStringRef(&getCellText->Text, L"Image digital signature support disabled.");
                }
                break;
            case PHPRTLC_ASLR:
                {
                    PhpUpdateProcessNodeImage(node);

                    if (FlagOn(node->ImageDllCharacteristics, IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
                        PhInitializeStringRef(&getCellText->Text, L"ASLR");
                    else
                        PhInitializeEmptyStringRef(&getCellText->Text);
                }
                break;
            case PHPRTLC_RELATIVESTARTTIME:
                {
                    if (processItem->CreateTime.QuadPart != 0)
                    {
                        LARGE_INTEGER currentTime;

                        PhQuerySystemTime(&currentTime);

                        if (PhGetIntegerSetting(L"EnableShortRelativeStartTime"))
                        {
                            if (processItem->CreateTime.QuadPart < currentTime.QuadPart)
                            {
                                ULONG64 relativeCreateTime = currentTime.QuadPart - processItem->CreateTime.QuadPart;
                                PhMoveReference(&node->RelativeStartTimeText, PhFormatTimeSpan(relativeCreateTime, PH_TIMESPAN_DHMS));
                            }
                            else
                            {
                                PhMoveReference(&node->RelativeStartTimeText, PhCreateString(L"\u221E"));
                            }
                        }
                        else
                        {
                            PPH_STRING startTimeString;

                            if (processItem->CreateTime.QuadPart < currentTime.QuadPart)
                            {
                                startTimeString = PhFormatTimeSpanRelative(currentTime.QuadPart - processItem->CreateTime.QuadPart);
                                PhMoveReference(&node->RelativeStartTimeText, PhConcatStringRefZ(&startTimeString->sr, L" ago"));
                                PhDereferenceObject(startTimeString);
                            }
                            else
                            {
                                PhMoveReference(&node->RelativeStartTimeText, PhCreateString(L"\u221E"));
                            }
                        }

                        getCellText->Text = node->RelativeStartTimeText->sr;
                    }
                }
                break;
            case PHPRTLC_BITS:
                {
#ifdef _WIN64
                    PhInitializeStringRef(&getCellText->Text, processItem->IsWow64Process ? L"32" : L"64");
#else
                    PhInitializeStringRef(&getCellText->Text, L"32");
#endif
                }
                break;
            case PHPRTLC_ELEVATION:
                {
                    PPH_STRINGREF elevationType;

                    if (PhGetElevationTypeString(!!processItem->IsElevated, processItem->ElevationType, &elevationType))
                    {
                        getCellText->Text.Buffer = elevationType->Buffer;
                        getCellText->Text.Length = elevationType->Length;
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                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");
                    else
                        PhInitializeEmptyStringRef(&getCellText->Text);
                }
                break;
            case PHPRTLC_CYCLES:
                {
                    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;
                    }
                }
                break;
            case PHPRTLC_CYCLESDELTA:
                {
                    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;
                    }
                }
                break;
            case PHPRTLC_DEP:
                {
                    PH_STRING_BUILDER sb;

                    PhpUpdateProcessNodeDepStatus(node);

                    PhInitializeStringBuilder(&sb, 20);

                    if (FlagOn(node->DepStatus, PH_PROCESS_DEP_ENABLED))
                    {
                        if (FlagOn(node->DepStatus, PH_PROCESS_DEP_PERMANENT))
                            PhAppendStringBuilder2(&sb, L"DEP (permanent), ");
                        else
                            PhAppendStringBuilder2(&sb, L"DEP, ");
                    }

                    if (FlagOn(node->DepStatus, PH_PROCESS_DEP_ATL_THUNK_EMULATION_DISABLED))
                        PhAppendStringBuilder2(&sb, L"ATL emulation, ");
                    if (FlagOn(node->DepStatus, PH_PROCESS_DEP_EXECUTE_ENABLED))
                        PhAppendStringBuilder2(&sb, L"Execute enabled, ");
                    if (FlagOn(node->DepStatus, PH_PROCESS_DEP_IMAGE_ENABLED))
                        PhAppendStringBuilder2(&sb, L"Image enabled, ");
                    if (FlagOn(node->DepStatus, PH_PROCESS_DEP_DISABLE_EXCEPTION_CHAIN))
                        PhAppendStringBuilder2(&sb, L"Chain disabled, ");

                    if (PhEndsWithString2(sb.String, L", ", FALSE))
                        PhRemoveEndStringBuilder(&sb, 2);

                    PhMoveReference(&node->DepStatusText, PhFinalStringBuilderString(&sb));
                    getCellText->Text = node->DepStatusText->sr;
                }
                break;
            case PHPRTLC_VIRTUALIZED:
                {
                    PhpUpdateProcessNodeToken(node);

                    if (node->VirtualizationEnabled)
                        PhInitializeStringRef(&getCellText->Text, L"Virtualized");
                    else
                        PhInitializeEmptyStringRef(&getCellText->Text);
                }
                break;
            case PHPRTLC_CONTEXTSWITCHES:
                {
                    ULONG value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, ContextSwitchesDelta.Value), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->ContextSwitchesText, PhFormatUInt64(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
                    {
                        ULONG value = 0;
                        PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, ContextSwitchesDelta.Delta), &value);

                        if (value != 0)
                        {
                            PhMoveReference(&node->ContextSwitchesDeltaText, PhFormatUInt64(value, TRUE));
                            getCellText->Text = node->ContextSwitchesDeltaText->sr;
                        }
                    }
                }
                break;
            case PHPRTLC_PAGEFAULTSDELTA:
                {
                    ULONG value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, PageFaultsDelta.Delta), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->PageFaultsDeltaText, PhFormatUInt64(value, TRUE));
                        getCellText->Text = node->PageFaultsDeltaText->sr;
                    }
                }
                break;
            case PHPRTLC_IOREADS:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadCountDelta.Value), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[0], PhFormatUInt64(value, TRUE));
                        getCellText->Text = node->IoGroupText[0]->sr;
                    }
                }
                break;
            case PHPRTLC_IOWRITES:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteCountDelta.Value), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[1], PhFormatUInt64(value, TRUE));
                        getCellText->Text = node->IoGroupText[1]->sr;
                    }
                }
                break;
            case PHPRTLC_IOOTHER:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherCountDelta.Value), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[2], PhFormatUInt64(value, TRUE));
                        getCellText->Text = node->IoGroupText[2]->sr;
                    }
                }
                break;
            case PHPRTLC_IOREADBYTES:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Value), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[3], PhFormatSize(value, ULONG_MAX));
                        getCellText->Text = node->IoGroupText[3]->sr;
                    }
                }
                break;
            case PHPRTLC_IOWRITEBYTES:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Value), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[4], PhFormatSize(value, ULONG_MAX));
                        getCellText->Text = node->IoGroupText[4]->sr;
                    }
                }
                break;
            case PHPRTLC_IOOTHERBYTES:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Value), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[5], PhFormatSize(value, ULONG_MAX));
                        getCellText->Text = node->IoGroupText[5]->sr;
                    }
                }
                break;
            case PHPRTLC_IOREADSDELTA:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadCountDelta.Delta), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[6], PhFormatUInt64(value, TRUE));
                        getCellText->Text = node->IoGroupText[6]->sr;
                    }
                }
                break;
            case PHPRTLC_IOWRITESDELTA:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteCountDelta.Delta), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[7], PhFormatUInt64(value, TRUE));
                        getCellText->Text = node->IoGroupText[7]->sr;
                    }
                }
                break;
            case PHPRTLC_IOOTHERDELTA:
                {
                    ULONG64 value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherCountDelta.Delta), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->IoGroupText[8], PhFormatUInt64(value, TRUE));
                        getCellText->Text = node->IoGroupText[8]->sr;
                    }
                }
                break;
            case PHPRTLC_OSCONTEXT:
                {
                    PhpUpdateProcessOsContext(node);
                    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;
                    }
                }
                break;
            case PHPRTLC_PAGEDPOOL:
                {
                    SIZE_T value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaPagedPoolUsage), &value);
                    PhMoveReference(&node->PagedPoolText, PhFormatSize(value, ULONG_MAX));
                    getCellText->Text = node->PagedPoolText->sr;
                }
                break;
            case PHPRTLC_PEAKPAGEDPOOL:
                {
                    PhMoveReference(&node->PeakPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPeakPagedPoolUsage, ULONG_MAX));
                    getCellText->Text = node->PeakPagedPoolText->sr;
                }
                break;
            case PHPRTLC_NONPAGEDPOOL:
                {
                    SIZE_T value = 0;
                    PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaNonPagedPoolUsage), &value);
                    PhMoveReference(&node->NonPagedPoolText, PhFormatSize(value, ULONG_MAX));
                    getCellText->Text = node->NonPagedPoolText->sr;
                }
                break;
            case PHPRTLC_PEAKNONPAGEDPOOL:
                {
                    PhMoveReference(&node->PeakNonPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPeakNonPagedPoolUsage, ULONG_MAX));
                    getCellText->Text = node->PeakNonPagedPoolText->sr;
                }
                break;
            case PHPRTLC_MINIMUMWORKINGSET:
                {
                    SIZE_T value = 0;
                    PhpUpdateProcessNodeQuotaLimits(node);
                    PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, MinimumWorkingSetSize), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->MinimumWorkingSetText, PhFormatSize(value, ULONG_MAX));
                        getCellText->Text = node->MinimumWorkingSetText->sr;
                    }
                }
                break;
            case PHPRTLC_MAXIMUMWORKINGSET:
                {
                    SIZE_T value = 0;
                    PhpUpdateProcessNodeQuotaLimits(node);
                    PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, MaximumWorkingSetSize), &value);

                    if (value != 0)
                    {
                        PhMoveReference(&node->MaximumWorkingSetText, PhFormatSize(value, ULONG_MAX));
                        getCellText->Text = node->MaximumWorkingSetText->sr;
                    }
                }
                break;
            case PHPRTLC_PRIVATEBYTESDELTA:
                {
                    LONG_PTR delta = 0;

                    PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, PrivateBytesDelta.Delta), &delta);

                    if (delta != 0)
                    {
                        PH_FORMAT format[2];

                        if (delta > 0)
                        {
                            PhInitFormatC(&format[0], L'+');
                        }
                        else
                        {
                            PhInitFormatC(&format[0], L'-');
                            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 PH_PROCESS_DPI_AWARENESS_UNAWARE + 1:
                        PhInitializeStringRef(&getCellText->Text, L"Unaware");
                        break;
                    case PH_PROCESS_DPI_AWARENESS_SYSTEM_DPI_AWARE + 1:
                        PhInitializeStringRef(&getCellText->Text, L"System aware");
                        break;
                    case PH_PROCESS_DPI_AWARENESS_PER_MONITOR_DPI_AWARE + 1:
                        PhInitializeStringRef(&getCellText->Text, L"Per-monitor aware");
                        break;
                    case PH_PROCESS_DPI_AWARENESS_PER_MONITOR_AWARE_V2 + 1:
                        PhInitializeStringRef(&getCellText->Text, L"Per-monitor V2");
                        break;
                    case PH_PROCESS_DPI_AWARENESS_UNAWARE_GDISCALED + 1:
                        PhInitializeStringRef(&getCellText->Text, L"Unaware (GDI scaled)");
                        break;
                    }
                }
                break;
            case PHPRTLC_CFGUARD:
                {
                    if (processItem->IsXfgAuditEnabled)
                        PhInitializeStringRef(&getCellText->Text, L"XF Guard (audit)");
                    else if (processItem->IsXfgEnabled)
                        PhInitializeStringRef(&getCellText->Text, L"XF Guard");
                    else if (processItem->IsControlFlowGuardEnabled)
                        PhInitializeStringRef(&getCellText->Text, L"CF Guard");
                    else
                        PhInitializeEmptyStringRef(&getCellText->Text);
                }
                break;
            case PHPRTLC_TIMESTAMP:
                {
                    PhpUpdateProcessNodeImage(node);

                    if (node->ImageTimeDateStamp != 0)
                    {
                        LARGE_INTEGER time;
                        SYSTEMTIME systemTime;

                        PhSecondsSince1970ToTime(node->ImageTimeDateStamp, &time);
                        PhLargeIntegerToLocalSystemTime(&systemTime, &time);

                        PhMoveReference(&node->TimeStampText, PhFormatDateTime(&systemTime));
                        getCellText->Text = node->TimeStampText->sr;
                    }
                }
                break;
            case PHPRTLC_FILEMODIFIEDTIME:
                {
                    PhpUpdateProcessNodeFileAttributes(node);

                    if (node->FileLastWriteTime.QuadPart != 0)
                    {
                        SYSTEMTIME systemTime;

                        PhLargeIntegerToLocalSystemTime(&systemTime, &node->FileLastWriteTime);
                        PhMoveReference(&node->FileModifiedTimeText, PhFormatDateTime(&systemTime));
                        getCellText->Text = node->FileModifiedTimeText->sr;
                    }
                }
                break;
            case PHPRTLC_FILESIZE:
                {
                    PhpUpdateProcessNodeFileAttributes(node);

                    if (node->FileEndOfFile.QuadPart != 0)
                    {
                        PhMoveReference(&node->FileSizeText, PhFormatSize(node->FileEndOfFile.QuadPart, ULONG_MAX));
                        getCellText->Text = node->FileSizeText->sr;
                    }
                }
                break;
            case PHPRTLC_SUBPROCESSCOUNT:
                {
                    if (node->Children && node->Children->Count != 0)
                    {
                        PhMoveReference(&node->SubprocessCountText, PhFormatUInt64(node->Children->Count, TRUE));
                        getCellText->Text = node->SubprocessCountText->sr;
                    }
                }
                break;
            case PHPRTLC_JOBOBJECTID:
                {
                    if (processItem->JobObjectId != 0)
                    {
                        PhMoveReference(&node->JobObjectIdText, PhFormatUInt64(processItem->JobObjectId, TRUE));
                        getCellText->Text = node->JobObjectIdText->sr;
                    }
                }
                break;
            case PHPRTLC_PROTECTION:
                {
                    if (processItem->Protection.Level ||
                        processItem->IsSecureProcess ||
                        processItem->IsProtectedProcess)
                    {
                        getCellText->Text = processItem->ProtectionString->sr;
                    }
                }
                break;
            case PHPRTLC_DESKTOP:
                {
                    PhpUpdateProcessNodeDesktopInfo(node);
                    getCellText->Text = PhGetStringRef(node->DesktopInfoText);
                }
                break;
            case PHPRTLC_CRITICAL:
                {
                    PhpUpdateProcessBreakOnTermination(node);

                    if (node->BreakOnTerminationEnabled)
                        PhInitializeStringRef(&getCellText->Text, L"Critical");
                    else
                        PhInitializeEmptyStringRef(&getCellText->Text);
                }
                break;
            case PHPRTLC_PIDHEX:
                {
                    if (PH_IS_REAL_PROCESS_ID(processItem->ProcessId))
                    {
                        PhInitializeStringRefLongHint(&getCellText->Text, processItem->ProcessIdHexString);
                    }
                }
                break;
            case PHPRTLC_CPUCORECYCLES:
                {
                    FLOAT cpuUsage = 0;

                    PhpAggregateFieldIfNeeded(node, AggregateTypeFloat, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuUsage), &cpuUsage);

                    cpuUsage *= 100;
                    cpuUsage = cpuUsage * PhCountBitsUlongPtr(processItem->AffinityMask);

                    if (cpuUsage >= PhMaxPrecisionLimit)
                    {
                        PH_FORMAT format;

                        PhInitFormatF(&format, cpuUsage, PhMaxPrecisionUnit);

                        PhMoveReference(&node->CpuCoreUsageText, PhFormat(&format, 1, 0));
                        getCellText->Text = node->CpuCoreUsageText->sr;
                    }
                    else if (cpuUsage != 0 && PhCsShowCpuBelow001)
                    {
                        PH_FORMAT format[2];

                        PhInitFormatS(&format[0], L"< ");
                        PhInitFormatF(&format[1], cpuUsage, PhMaxPrecisionUnit);

                        PhMoveReference(&node->CpuCoreUsageText, PhFormat(format, RTL_NUMBER_OF(format), 0));
                        getCellText->Text = node->CpuCoreUsageText->sr;
                    }
                }
                break;
            case PHPRTLC_CET:
                {
                    if (processItem->IsCetEnabled)
                        PhInitializeStringRef(&getCellText->Text, L"CET");
                    else
                        PhInitializeEmptyStringRef(&getCellText->Text);
                }
                break;
            case PHPRTLC_IMAGE_COHERENCY:
                {
                    PH_FORMAT format[2];

                    if (!PhEnableImageCoherencySupport)
                    {
                        PhInitializeStringRef(&getCellText->Text, L"Image coherency support is disabled.");
                        break;
                    }

                    if (processItem->ImageCoherencyStatus == STATUS_PENDING)
                    {
                        PhInitializeStringRef(&getCellText->Text, L"Scanning....");
                        break;
                    }

                    if (!PhpShouldShowImageCoherency(processItem, FALSE))
                    {
                        if (processItem->ImageCoherencyStatus != STATUS_SUCCESS)
                        {
                            PhMoveReference(
                                &node->ImageCoherencyStatusText,
                                PhGetStatusMessage(processItem->ImageCoherencyStatus, 0)
                                );
                            getCellText->Text = PhGetStringRef(node->ImageCoherencyStatusText);
                        }
                        break;
                    }

                    if (processItem->ImageCoherency == 1.0f)
                    {
                        PhInitializeStringRef(&getCellText->Text, L"100%");
                        break;
                    }
                    if (processItem->ImageCoherency > 0.9999f)
                    {
                        PhInitializeStringRef(&getCellText->Text, L">99.99%");
                        break;
                    }

                    PhInitFormatF(&format[0], (processItem->ImageCoherency * 100.f), PhMaxPrecisionUnit);
                    PhInitFormatS(&format[1], L"%");

                    PhMoveReference(&node->ImageCoherencyText, PhFormat(format, RTL_NUMBER_OF(format), 0));
                    getCellText->Text = node->ImageCoherencyText->sr;
                }
                break;
            case PHPRTLC_ERRORMODE:
                {
                    PhpUpdateProcessNodeErrorMode(node);
                    getCellText->Text = PhGetStringRef(node->ErrorModeText);
                }
                break;
            case PHPRTLC_CODEPAGE:
                {
                    PhpUpdateProcessNodeCodePage(node);

                    if (node->CodePage != 0)
                    {
                        PhMoveReference(&node->CodePageText, PhFormatUInt64(node->CodePage, FALSE));
                        getCellText->Text = node->CodePageText->sr;
                    }
                }
                break;
            case PHPRTLC_POWERTHROTTLING:
                {
                    PhpUpdateProcessNodePowerThrottling(node);

                    if (node->PowerThrottling)
                        PhInitializeStringRef(&getCellText->Text, L"Yes");
                    else
                        PhInitializeEmptyStringRef(&getCellText->Text);
                }
                break;
            case PHPRTLC_ARCHITECTURE:
                {
                    PhpUpdateProcessNodeImage(node);

                    // Use Architecture from PhGetProcessArchitecture if we have it, this supports
                    // cases where the process is executing as a different architecture than the
                    // image it was loaded from (ARM64X or .NET).
                    if (node->Architecture != IMAGE_FILE_MACHINE_UNKNOWN)
                    {
                        switch (node->Architecture)
                        {
                        case IMAGE_FILE_MACHINE_I386:
                            PhInitializeStringRef(&getCellText->Text, L"x86");
                            break;
                        case IMAGE_FILE_MACHINE_AMD64:
#ifdef _ARM64_
                            PhInitializeStringRef(&getCellText->Text, node->ImageCHPEVersion ? L"x64 (ARM64X)" : L"x64");
#else
                            PhInitializeStringRef(&getCellText->Text, L"x64");
#endif
                            break;
                        case IMAGE_FILE_MACHINE_ARMNT:
                            PhInitializeStringRef(&getCellText->Text, L"ARM");
                            break;
                        case IMAGE_FILE_MACHINE_ARM64:
#ifdef _ARM64_
                            PhInitializeStringRef(&getCellText->Text, node->ImageCHPEVersion ? L"ARM64 (ARM64X)" : L"ARM64");
#else
                            PhInitializeStringRef(&getCellText->Text, L"ARM64");
#endif
                            break;
                        }
                    }
                    else
                    {
                        switch (node->ImageMachine)
                        {
                        case IMAGE_FILE_MACHINE_I386:
                            PhInitializeStringRef(&getCellText->Text, L"x86");
                            break;
                        case IMAGE_FILE_MACHINE_AMD64:
#ifdef _ARM64_
                            PhInitializeStringRef(&getCellText->Text, node->ImageCHPEVersion ? L"x64 (ARM64X)" : L"x64");
#else
                            PhInitializeStringRef(&getCellText->Text, L"x64");
#endif
                            break;
                        case IMAGE_FILE_MACHINE_ARMNT:
                            PhInitializeStringRef(&getCellText->Text, L"ARM");
                            break;
                        case IMAGE_FILE_MACHINE_ARM64:
#ifdef _ARM64_
                            PhInitializeStringRef(&getCellText->Text, node->ImageCHPEVersion ? L"ARM64 (ARM64X)" : L"ARM64");
#else
                            PhInitializeStringRef(&getCellText->Text, L"ARM64");
#endif
                            break;
                        }
                    }
                    break;
                }
                break;
            case PHPRTLC_PARENTPID:
                {
                    if (PH_IS_REAL_PROCESS_ID(processItem->ProcessId))
                    {
                        PH_FORMAT format;

                        PhInitFormatU(&format, HandleToUlong(processItem->ParentProcessId));

                        PhMoveReference(&node->ParentPidText, PhFormat(&format, 1, 0));
                        getCellText->Text = node->ParentPidText->sr;
                    }
                }
                break;
            case PHPRTLC_PARENTCONSOLEPID:
                {
                    PH_FORMAT format;

                    PhInitFormatU(&format, HandleToUlong((HANDLE)((ULONG_PTR)processItem->ConsoleHostProcessId & ~3)));

                    PhMoveReference(&node->ParentConsolePidText, PhFormat(&format, 1, 0));
                    getCellText->Text = node->ParentConsolePidText->sr;
                }
                break;
            case PHPRTLC_COMMITSIZE:
                {
                    if (processItem->SharedCommitCharge != 0)
                    {
                        PhMoveReference(&node->SharedCommitText, PhFormatSize(processItem->SharedCommitCharge, ULONG_MAX));
                        getCellText->Text = node->SharedCommitText->sr;
                    }
                }
                break;
            case PHPRTLC_PRIORITYBOOST:
                {
                    PhpUpdateProcessNodePriorityBoost(node);

                    if (node->PriorityBoost)
                        PhInitializeStringRef(&getCellText->Text, L"Yes");
                    else
                        PhInitializeEmptyStringRef(&getCellText->Text);
                }
                break;
            case PHPRTLC_CPUAVERAGE:
                {
                    FLOAT cpuUsage;

                    cpuUsage = processItem->CpuAverageUsage * 100;

                    if (cpuUsage >= PhMaxPrecisionLimit)
                    {
                        PH_FORMAT format;

                        PhInitFormatF(&format, cpuUsage, PhMaxPrecisionUnit);

                        PhMoveReference(&node->CpuAverageText, PhFormat(&format, 1, 0));
                        getCellText->Text = node->CpuAverageText->sr;
                    }
                    else if (cpuUsage != 0 && PhCsShowCpuBelow001)
                    {
                        PH_FORMAT format[2];

                        PhInitFormatS(&format[0], L"< ");
                        PhInitFormatF(&format[1], cpuUsage, PhMaxPrecisionUnit);

                        PhMoveReference(&node->CpuAverageText, PhFormat(format, 2, 0));
                        getCellText->Text = node->CpuAverageText->sr;
                    }
                }
                break;
            case PHPRTLC_CPUKERNEL:
                {
                    FLOAT cpuUsage;

                    cpuUsage = processItem->CpuKernelUsage * 100;

                    if (cpuUsage >= PhMaxPrecisionLimit)
                    {
                        PH_FORMAT format;

                        PhInitFormatF(&format, cpuUsage, PhMaxPrecisionUnit);

                        PhMoveReference(&node->CpuKernelText, PhFormat(&format, 1, 0));
                        getCellText->Text = node->CpuKernelText->sr;
                    }
                    else if (cpuUsage != 0 && PhCsShowCpuBelow001)
                    {
                        PH_FORMAT format[2];

                        PhInitFormatS(&format[0], L"< ");
                        PhInitFormatF(&format[1], cpuUsage, PhMaxPrecisionUnit);

                        PhMoveReference(&node->CpuKernelText, PhFormat(format, RTL_NUMBER_OF(format), 0));
                        getCellText->Text = node->CpuKernelText->sr;
                    }
                }
                break;
            case PHPRTLC_CPUUSER:
                {
                    FLOAT cpuUsage;

                    cpuUsage = processItem->CpuUserUsage * 100;

                    if (cpuUsage >= PhMaxPrecisionLimit)
                    {
                        PH_FORMAT format;

                        PhInitFormatF(&format, cpuUsage, PhMaxPrecisionUnit);

                        PhMoveReference(&node->CpuUserText, PhFormat(&format, 1, 0));
                        getCellText->Text = node->CpuUserText->sr;
                    }
                    else if (cpuUsage != 0 && PhCsShowCpuBelow001)
                    {
                        PH_FORMAT format[2];

                        PhInitFormatS(&format[0], L"< ");
                        PhInitFormatF(&format[1], cpuUsage, PhMaxPrecisionUnit);

                        PhMoveReference(&node->CpuUserText, PhFormat(format, RTL_NUMBER_OF(format), 0));
                        getCellText->Text = node->CpuUserText->sr;
                    }
                }
                break;
            case PHPRTLC_GRANTEDACCESS:
                {
                    PhpUpdateProcessNodeGrantedAccess(node);
                    getCellText->Text = PhGetStringRef(node->GrantedAccessText);
                }
                break;
            case PHPRTLC_TLSBITMAPDELTA:
                {
                    PhpUpdateProcessNodeTlsBitmapDelta(node);

                    if (node->TlsBitmapCount != 0)
                    {
                        if (node->TlsBitmapCount > TLS_MINIMUM_AVAILABLE)
                        {
                            PH_FORMAT format[8];

                            // 64 (100%) | 1024 (100%)
                            PhInitFormatU(&format[0], 64);
                            PhInitFormatS(&format[1], L" (");
                            PhInitFormatF(&format[2], 100.0f, 2);
                            PhInitFormatS(&format[3], L"%) | ");
                            PhInitFormatU(&format[4], node->TlsBitmapCount - TLS_MINIMUM_AVAILABLE);
                            PhInitFormatS(&format[5], L" (");
                            PhInitFormatF(&format[6], (node->TlsBitmapCount - TLS_MINIMUM_AVAILABLE) * 100.f / TLS_EXPANSION_SLOTS, 2);
                            PhInitFormatS(&format[7], L"%)");

                            PhMoveReference(&node->TlsBitmapDeltaText, PhFormat(format, RTL_NUMBER_OF(format), 0));
                        }
                        else
                        {
                            PH_FORMAT format[8];

                            // 64 (100%) | 0 (0%)
                            PhInitFormatU(&format[0], node->TlsBitmapCount);
                            PhInitFormatS(&format[1], L" (");
                            PhInitFormatF(&format[2], node->TlsBitmapCount * 100.f / TLS_MINIMUM_AVAILABLE, 2);
                            PhInitFormatS(&format[3], L"%) | ");
                            PhInitFormatU(&format[4], 0);
                            PhInitFormatS(&format[5], L" (");
                            PhInitFormatF(&format[6], 0.0f, 2);
                            PhInitFormatS(&format[7], L"%)");

                            PhMoveReference(&node->TlsBitmapDeltaText, PhFormat(format, RTL_NUMBER_OF(format), 0));
                        }

                        getCellText->Text = PhGetStringRef(node->TlsBitmapDeltaText);
                    }
                }
                break;
            case PHPRTLC_REFERENCEDELTA:
                {
                    PhpUpdateProcessNodeObjectReferences(node);

                    if (node->ReferenceCount != 0)
                    {
                        PhMoveReference(&node->ReferenceCountText, PhFormatUInt64(node->ReferenceCount, FALSE));
                        getCellText->Text = node->ReferenceCountText->sr;
                    }
                }
                break;
            case PHPRTLC_LXSSPID:
                {
                    if (processItem->LxssProcessId)
                    {
                        PH_FORMAT format;

                        PhInitFormatU(&format, processItem->LxssProcessId);

                        PhMoveReference(&node->LxssProcessIdText, PhFormat(&format, 1, 0));
                        getCellText->Text = PhGetStringRef(node->LxssProcessIdText);
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                break;
            case PHPRTLC_START_KEY:
                {
                    PhpUpdateProcessNodeStartKey(node);

                    if (node->ProcessStartKey != 0)
                    {
                        getCellText->Text = PhGetStringRef(node->ProcessStartKeyText);
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                break;
            case PHPRTLC_MITIGATION_POLICIES:
                {
                    PhpUpdateProcessNodeMitigationPolicies(node);

                    if (node->MitigationPoliciesText)
                    {
                        getCellText->Text = PhGetStringRef(node->MitigationPoliciesText);
                    }
                    else
                    {
                        PhInitializeEmptyStringRef(&getCellText->Text);
                    }
                }
                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.ForeColor = RGB(0x00, 0x00, 0x00);
                getHighlightingColor.Handled = FALSE;
                getHighlightingColor.Cache = FALSE;

                PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), &getHighlightingColor);

                if (getHighlightingColor.Handled)
                {
                    getNodeColor->BackColor = getHighlightingColor.BackColor;
                    getNodeColor->ForeColor = getHighlightingColor.ForeColor;

                    if (!getNodeColor->ForeColor)
                        getNodeColor->Flags |= TN_AUTO_FORECOLOR;

                    if (getHighlightingColor.Cache)
                        getNodeColor->Flags |= TN_CACHE;

                    return TRUE;
                }
            }

            getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR;

            if (!processItem)
                ; // Dummy
            else if (PhCsUseColorDebuggedProcesses && processItem->IsBeingDebugged)
                getNodeColor->BackColor = PhCsColorDebuggedProcesses;
            else if (PhCsUseColorSuspended && processItem->IsSuspended)
                getNodeColor->BackColor = PhCsColorSuspended;
            else if (PhCsUseColorPartiallySuspended && processItem->ProcessId != SYSTEM_PROCESS_ID && processItem->IsPartiallySuspended)
                getNodeColor->BackColor = PhCsColorPartiallySuspended;
            else if (PhCsUseColorProtectedProcess && processItem->Protection.Level != 0 && processItem->Protection.Level != UCHAR_MAX)
                getNodeColor->BackColor = PhCsColorProtectedProcess;
            else if (PhCsUseColorHandleFiltered && (processItem->IsProtectedHandle || processItem->ProcessId == NtCurrentProcessId()))
                getNodeColor->BackColor = PhCsColorHandleFiltered;
            else if (PhCsUseColorElevatedProcesses && processItem->IsElevated && processItem->ElevationType == TokenElevationTypeFull)
                getNodeColor->BackColor = PhCsColorElevatedProcesses;
            else if (PhCsUseColorUIAccessProcesses && processItem->IsUIAccessEnabled)
                getNodeColor->BackColor = PhCsColorUIAccessProcesses;
            else if (PhCsUseColorPicoProcesses && processItem->IsSubsystemProcess)
                getNodeColor->BackColor = PhCsColorPicoProcesses;
            else if (PhCsUseColorEfficiencyMode && processItem->IsPowerThrottling)
                getNodeColor->BackColor = PhCsColorEfficiencyMode;
            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 (PhCsUseColorLowImageCoherency && PhEnableImageCoherencySupport && PhpShouldShowImageCoherency(processItem, TRUE))
                getNodeColor->BackColor = PhCsColorLowImageCoherency;
            else if (PhCsUseColorBackgroundProcesses && PhIsProcessBackground(processItem->PriorityClass))
                getNodeColor->BackColor = PhCsColorBackgroundProcesses;
            else if (PhCsUseColorWow64Processes && processItem->IsWow64Process)
                getNodeColor->BackColor = PhCsColorWow64Processes;
            else if (PhCsUseColorJobProcesses && processItem->IsInSignificantJob)
                getNodeColor->BackColor = PhCsColorJobProcesses;
            else if (
                PhCsUseColorSystemProcesses &&
                (processItem->IsSystemProcess ||
                (processItem->Sid && PhEqualSid(processItem->Sid, (PSID)&PhSeLocalSystemSid)) ||
                PH_IS_FAKE_PROCESS_ID(processItem->ProcessId) || processItem->ProcessId == SYSTEM_PROCESS_ID
                ))
                getNodeColor->BackColor = PhCsColorSystemProcesses;
            else if (
                PhCsUseColorOwnProcesses &&
                processItem->Sid &&
                PhEqualSid(processItem->Sid, PhGetOwnTokenAttributes().TokenSid)
                )
                getNodeColor->BackColor = PhCsColorOwnProcesses;
            else if (
                PhCsUseColorServiceProcesses &&
                ((processItem->ServiceList && processItem->ServiceList->Count != 0) ||
                (processItem->Sid && PhEqualSid(processItem->Sid, (PSID)&PhSeServiceSid)) ||
                (processItem->Sid && PhEqualSid(processItem->Sid, (PSID)&PhSeLocalServiceSid)) ||
                (processItem->Sid && PhEqualSid(processItem->Sid, (PSID)&PhSeNetworkServiceSid))
                ))
                getNodeColor->BackColor = PhCsColorServiceProcesses;
        }
        return TRUE;
    case TreeNewGetNodeIcon:
        {
            PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1;

            node = (PPH_PROCESS_NODE)getNodeIcon->Node;
            getNodeIcon->Icon = (HICON)(ULONG_PTR)node->ProcessItem->SmallIconIndex;
            //getNodeIcon->Flags = TN_CACHE;
        }
        return TRUE;
    case TreeNewGetCellTooltip:
        {
            PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1;
            ULONG64 tickCount;

            node = (PPH_PROCESS_NODE)getCellTooltip->Node;

            if (getCellTooltip->Column->Id != 0)
                return FALSE;

            tickCount = NtGetTickCount64();

            if ((LONG64)(node->TooltipTextValidToTickCount - tickCount) < 0)
                PhClearReference(&node->TooltipText);

            if (PhEnableTooltipSupport && !node->TooltipText)
                node->TooltipText = PhGetProcessTooltipText(node->ProcessItem, &node->TooltipTextValidToTickCount);

            if (!PhIsNullOrEmptyString(node->TooltipText))
            {
                getCellTooltip->Text = node->TooltipText->sr;
                getCellTooltip->Unfolding = FALSE;
                getCellTooltip->MaximumWidth = ULONG_MAX;
            }
            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);

                        if (PhCsEnableGraphMaxScale)
                        {
                            FLOAT max = 0;

                            if (PhCsEnableAvxSupport && drawInfo.LineDataCount > 128)
                            {
                                max = PhAddPlusMaxMemorySingles(
                                    node->CpuGraphBuffers.Data1,
                                    node->CpuGraphBuffers.Data2,
                                    drawInfo.LineDataCount
                                    );
                            }
                            else
                            {
                                for (ULONG i = 0; i < drawInfo.LineDataCount; i++)
                                {
                                    FLOAT data = node->CpuGraphBuffers.Data1[i] + node->CpuGraphBuffers.Data2[i];

                                    if (max < data)
                                        max = data;
                                }
                            }

                            if (max != 0)
                            {
                                PhDivideSinglesBySingle(
                                    node->CpuGraphBuffers.Data1,
                                    max,
                                    drawInfo.LineDataCount
                                    );
                                PhDivideSinglesBySingle(
                                    node->CpuGraphBuffers.Data2,
                                    max,
                                    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)
                    {
                        FLOAT total;
                        FLOAT max;

                        for (ULONG 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)
                    {
                        FLOAT total;
                        FLOAT max = 0;

                        for (ULONG 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;
            case PHPRTLC_TIMELINE:
                {
                    if (PH_IS_REAL_PROCESS_ID(processItem->ProcessId))
                    {
                        PhCustomDrawTreeTimeLine(
                            customDraw->Dc,
                            customDraw->CellRect,
                            PhEnableThemeSupport ? PH_DRAW_TIMELINE_DARKTHEME : 0,
                            NULL,
                            &processItem->CreateTime
                            );
                    }
                    else
                    {
                        LARGE_INTEGER createTime;

                        // DPCs, Interrupts and System Idle Process are always 100%
                        createTime.QuadPart = 0;

                        PhCustomDrawTreeTimeLine(
                            customDraw->Dc,
                            customDraw->CellRect,
                            PhEnableThemeSupport ? PH_DRAW_TIMELINE_DARKTHEME : 0,
                            NULL,
                            &createTime
                            );
                    }
                }
                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:
        {
            PPH_TREENEW_SORT_CHANGED_EVENT sorting = Parameter1;

            ProcessTreeListSortColumn = sorting->SortColumn;
            ProcessTreeListSortOrder = sorting->SortOrder;

            // 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 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.Selection)
            {
                if (data.Selection->Id == PH_TN_COLUMN_MENU_HIDE_COLUMN_ID ||
                    data.Selection->Id == PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID)
                {
                    PhReloadSettingsProcessTreeList();
                }
            }

            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;
    case TreeNewGetHeaderText:
        {
            PPH_TREENEW_GET_HEADER_TEXT getHeaderText = Parameter1;
            PPH_TREENEW_COLUMN column = getHeaderText->Column;
            SIZE_T returnLength;
            FLOAT decimal = 0;
            ULONG64 number = 0;

            switch (column->Id)
            {
            case PHPRTLC_CPU:
            case PHPRTLC_IOTOTALRATE:
            case PHPRTLC_PRIVATEBYTES:
            case PHPRTLC_PEAKPRIVATEBYTES:
            case PHPRTLC_WORKINGSET:
            case PHPRTLC_PEAKWORKINGSET:
            case PHPRTLC_PRIVATEWS:
            case PHPRTLC_VIRTUALSIZE:
            case PHPRTLC_PEAKVIRTUALSIZE:
            case PHPRTLC_PAGEFAULTS:
            case PHPRTLC_THREADS:
            case PHPRTLC_HANDLES:
            case PHPRTLC_GDIHANDLES:
            case PHPRTLC_USERHANDLES:
            case PHPRTLC_IORORATE:
            case PHPRTLC_IOWRATE:
            case PHPRTLC_CYCLES:
            case PHPRTLC_CYCLESDELTA:
            case PHPRTLC_CONTEXTSWITCHES:
            case PHPRTLC_CONTEXTSWITCHESDELTA:
            case PHPRTLC_IOREADS:
            case PHPRTLC_IOWRITES:
            case PHPRTLC_IOOTHER:
            case PHPRTLC_IOREADBYTES:
            case PHPRTLC_IOWRITEBYTES:
            case PHPRTLC_IOOTHERBYTES:
            case PHPRTLC_IOREADSDELTA:
            case PHPRTLC_IOWRITESDELTA:
            case PHPRTLC_IOOTHERDELTA:
            case PHPRTLC_PAGEDPOOL:
            case PHPRTLC_PEAKPAGEDPOOL:
            case PHPRTLC_NONPAGEDPOOL:
            case PHPRTLC_PEAKNONPAGEDPOOL:
            case PHPRTLC_PRIVATEBYTESDELTA:
            case PHPRTLC_CPUCORECYCLES:
            case PHPRTLC_COMMITSIZE:
            case PHPRTLC_CPUAVERAGE:
            case PHPRTLC_CPUKERNEL:
            case PHPRTLC_CPUUSER:
                break;
            default:
                return FALSE;
            }

            // NOTE: Windows Task Manager doesn't loop subitems when summing the totals but instead just returns the system totals.
            // We should do the same for some columns like CPU when/if the below loop/aggregate fields becomes a performance issue. (dmex)

            for (ULONG i = 0; i < ProcessNodeList->Count; i++)
            {
                node = ProcessNodeList->Items[i];

                if (!node->Node.Visible)
                    continue; // Skip filtered.
                if (node->ProcessItem->State & PH_PROCESS_ITEM_REMOVED)
                    continue; // Skip terminated.
                if (!PhTestEvent(&node->ProcessItem->Stage1Event))
                    continue; // Skip unprocessed.

                switch (column->Id)
                {
                case PHPRTLC_CPU:
                    {
                        if (node->ProcessId == SYSTEM_IDLE_PROCESS_ID)
                            continue;

                        PhpAggregateFieldTotal(node, AggregateTypeFloat, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuUsage), &decimal);
                    }
                    break;
                case PHPRTLC_IOTOTALRATE:
                    {
                        if (node->ProcessItem->IoReadDelta.Delta != node->ProcessItem->IoReadDelta.Value) // delta is wrong on first run of process provider
                        {
                            PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Delta), &number);
                            PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Delta), &number);
                            PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Delta), &number);
                        }
                    }
                    break;
                case PHPRTLC_PRIVATEBYTES:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PagefileUsage), &number);
                    break;
                case PHPRTLC_PEAKPRIVATEBYTES:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PeakPagefileUsage), &number);
                    break;
                case PHPRTLC_WORKINGSET:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.WorkingSetSize), &number);
                    break;
                case PHPRTLC_PEAKWORKINGSET:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PeakWorkingSetSize), &number);
                    break;
                case PHPRTLC_PRIVATEWS:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, WorkingSetPrivateSize), &number);
                    break;
                //case PHPRTLC_SHAREDWS:
                //    node->WsCounters.NumberOfSharedPages
                //    break;
                //case PHPRTLC_SHAREABLEWS:
                //    node->WsCounters.NumberOfShareablePages
                //    break;
                case PHPRTLC_VIRTUALSIZE:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.VirtualSize), &number);
                    break;
                case PHPRTLC_PEAKVIRTUALSIZE:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PeakVirtualSize), &number);
                    break;
                case PHPRTLC_PAGEFAULTS:
                    PhpAggregateFieldTotal(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PageFaultCount), &number);
                    break;
                case PHPRTLC_THREADS:
                    PhpAggregateFieldTotal(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, NumberOfThreads), &number);
                    break;
                case PHPRTLC_HANDLES:
                    PhpAggregateFieldTotal(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, NumberOfHandles), &number);
                    break;
                case PHPRTLC_GDIHANDLES:
                    PhpAggregateFieldTotal(node, AggregateTypeInt32, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, GdiHandles), &number);
                    break;
                case PHPRTLC_USERHANDLES:
                    PhpAggregateFieldTotal(node, AggregateTypeInt32, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, UserHandles), &number);
                    break;
                case PHPRTLC_IORORATE:
                    {
                        if (node->ProcessItem->IoReadDelta.Delta != node->ProcessItem->IoReadDelta.Value)
                        {
                            PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Delta), &number);
                            PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Delta), &number);
                        }
                    }
                    break;
                case PHPRTLC_IOWRATE:
                    {
                        if (node->ProcessItem->IoReadDelta.Delta != node->ProcessItem->IoReadDelta.Value)
                        {
                            PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Delta), &number);
                        }
                    }
                    break;
                case PHPRTLC_CYCLES:
                    {
                        if (node->ProcessId == SYSTEM_IDLE_PROCESS_ID)
                            continue;

                        PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Value), &number);
                    }
                    break;
                case PHPRTLC_CYCLESDELTA:
                    {
                        if (node->ProcessId == SYSTEM_IDLE_PROCESS_ID)
                            continue;

                        PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Delta), &number);
                    }
                    break;
                case PHPRTLC_CONTEXTSWITCHES:
                    {
                        if (node->ProcessId == SYSTEM_IDLE_PROCESS_ID)
                            continue;

                        PhpAggregateFieldTotal(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, ContextSwitchesDelta.Value), &number);
                    }
                    break;
                case PHPRTLC_CONTEXTSWITCHESDELTA:
                    {
                        if ((LONG)node->ProcessItem->ContextSwitchesDelta.Delta >= 0) // the delta may be negative if a thread exits - just don't show anything
                        {
                            PhpAggregateFieldTotal(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, ContextSwitchesDelta.Delta), &number);
                        }
                    }
                    break;
                case PHPRTLC_IOREADS:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadCountDelta.Value), &number);
                    break;
                case PHPRTLC_IOWRITES:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteCountDelta.Value), &number);
                    break;
                case PHPRTLC_IOOTHER:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherCountDelta.Value), &number);
                    break;
                case PHPRTLC_IOREADBYTES:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Value), &number);
                    break;
                case PHPRTLC_IOWRITEBYTES:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Value), &number);
                    break;
                case PHPRTLC_IOOTHERBYTES:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Value), &number);
                    break;
                case PHPRTLC_IOREADSDELTA:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadCountDelta.Delta), &number);
                    break;
                case PHPRTLC_IOWRITESDELTA:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteCountDelta.Delta), &number);
                    break;
                case PHPRTLC_IOOTHERDELTA:
                    PhpAggregateFieldTotal(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherCountDelta.Delta), &number);
                    break;
                case PHPRTLC_PAGEDPOOL:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaPagedPoolUsage), &number);
                    break;
                case PHPRTLC_PEAKPAGEDPOOL:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaPeakPagedPoolUsage), &number);
                    break;
                case PHPRTLC_NONPAGEDPOOL:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaNonPagedPoolUsage), &number);
                    break;
                case PHPRTLC_PEAKNONPAGEDPOOL:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaPeakNonPagedPoolUsage), &number);
                    break;
                case PHPRTLC_PRIVATEBYTESDELTA:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, PrivateBytesDelta.Delta), &number);
                    break;
                case PHPRTLC_CPUCORECYCLES:
                    {
                        if (node->ProcessId == SYSTEM_IDLE_PROCESS_ID)
                            continue;

                        PhpAggregateFieldIfNeeded(node, AggregateTypeFloat, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuUsage), &decimal);
                    }
                    break;
                case PHPRTLC_COMMITSIZE:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, SharedCommitCharge), &number);
                    break;
                case PHPRTLC_CPUAVERAGE:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuAverageUsage), &number);
                    break;
                case PHPRTLC_CPUKERNEL:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuKernelUsage), &number);
                    break;
                case PHPRTLC_CPUUSER:
                    PhpAggregateFieldTotal(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuUserUsage), &number);
                    break;
                }
            }

            switch (column->Id)
            {
            case PHPRTLC_CPU:
                {
                    PH_FORMAT format[2];

                    if (decimal == 0.0)
                        break;

                    decimal *= 100;
                    PhInitFormatF(&format[0], decimal, PhMaxPrecisionUnit);
                    PhInitFormatC(&format[1], L'%');

                    if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), getHeaderText->TextCache, getHeaderText->TextCacheSize, &returnLength))
                    {
                        getHeaderText->Text.Buffer = getHeaderText->TextCache;
                        getHeaderText->Text.Length = returnLength - sizeof(UNICODE_NULL);
                    }
                }
                return TRUE;
            case PHPRTLC_IOTOTALRATE:
            case PHPRTLC_IORORATE:
            case PHPRTLC_IOWRATE:
                {
                    PH_FORMAT format[2];

                    if (number == 0)
                        break;

                    number *= 1000;
                    number /= PhCsUpdateInterval;
                    PhInitFormatSize(&format[0], number);
                    PhInitFormatS(&format[1], L"/s");

                    if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), getHeaderText->TextCache, getHeaderText->TextCacheSize, &returnLength))
                    {
                        getHeaderText->Text.Buffer = getHeaderText->TextCache;
                        getHeaderText->Text.Length = returnLength - sizeof(UNICODE_NULL);
                    }
                }
                return TRUE;
            case PHPRTLC_PEAKPRIVATEBYTES:
            case PHPRTLC_PRIVATEBYTES:
            case PHPRTLC_WORKINGSET:
            case PHPRTLC_PEAKWORKINGSET:
            case PHPRTLC_PRIVATEWS:
            case PHPRTLC_VIRTUALSIZE:
            case PHPRTLC_PEAKVIRTUALSIZE:
            case PHPRTLC_IOREADBYTES:
            case PHPRTLC_IOWRITEBYTES:
            case PHPRTLC_IOOTHERBYTES:
            case PHPRTLC_PAGEDPOOL:
            case PHPRTLC_PEAKPAGEDPOOL:
            case PHPRTLC_NONPAGEDPOOL:
            case PHPRTLC_PEAKNONPAGEDPOOL:
            case PHPRTLC_COMMITSIZE:
                {
                    PH_FORMAT format[1];

                    if (number == 0)
                        break;

                    PhInitFormatSize(&format[0], number);

                    if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), getHeaderText->TextCache, getHeaderText->TextCacheSize, &returnLength))
                    {
                        getHeaderText->Text.Buffer = getHeaderText->TextCache;
                        getHeaderText->Text.Length = returnLength - sizeof(UNICODE_NULL);
                    }
                }
                return TRUE;
            case PHPRTLC_PAGEFAULTS:
            case PHPRTLC_THREADS:
            case PHPRTLC_HANDLES:
            case PHPRTLC_GDIHANDLES:
            case PHPRTLC_USERHANDLES:
            case PHPRTLC_CYCLES:
            case PHPRTLC_CYCLESDELTA:
            case PHPRTLC_CONTEXTSWITCHES:
            case PHPRTLC_CONTEXTSWITCHESDELTA:
            case PHPRTLC_IOREADS:
            case PHPRTLC_IOWRITES:
            case PHPRTLC_IOOTHER:
            case PHPRTLC_IOREADSDELTA:
            case PHPRTLC_IOWRITESDELTA:
            case PHPRTLC_IOOTHERDELTA:
                {
                    PH_FORMAT format[1];

                    if (number == 0)
                        break;

                    PhInitFormatI64UGroupDigits(&format[0], number);

                    if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), getHeaderText->TextCache, getHeaderText->TextCacheSize, &returnLength))
                    {
                        getHeaderText->Text.Buffer = getHeaderText->TextCache;
                        getHeaderText->Text.Length = returnLength - sizeof(UNICODE_NULL);
                    }
                }
                return TRUE;
            case PHPRTLC_PRIVATEBYTESDELTA:
                {
                    if ((LONG_PTR)number != 0)
                    {
                        PH_FORMAT format[2];

                        if ((LONG_PTR)number > 0)
                        {
                            PhInitFormatC(&format[0], L'+');
                        }
                        else
                        {
                            PhInitFormatC(&format[0], L'-');
                            number = -(LONG_PTR)number;
                        }

                        format[1].Type = SizeFormatType | FormatUseRadix;
                        format[1].Radix = (UCHAR)PhMaxSizeUnit;
                        format[1].u.Size = (LONG_PTR)number;

                        if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), getHeaderText->TextCache, getHeaderText->TextCacheSize, &returnLength))
                        {
                            getHeaderText->Text.Buffer = getHeaderText->TextCache;
                            getHeaderText->Text.Length = returnLength - sizeof(UNICODE_NULL);
                        }
                    }
                }
                return TRUE;
            case PHPRTLC_CPUCORECYCLES:
                {
                    PH_FORMAT format[2];

                    if (decimal == 0)
                        break;

                    decimal *= 100;
                    decimal *= PhSystemProcessorInformation.NumberOfProcessors;

                    PhInitFormatF(&format[0], decimal, PhMaxPrecisionUnit);
                    PhInitFormatC(&format[1], L'%');

                    if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), getHeaderText->TextCache, getHeaderText->TextCacheSize, &returnLength))
                    {
                        getHeaderText->Text.Buffer = getHeaderText->TextCache;
                        getHeaderText->Text.Length = returnLength - sizeof(UNICODE_NULL);
                    }
                }
                return TRUE;
            }
        }
        return FALSE;
    case TreeNewMiddleClick:
        {
            PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1;
            node = (PPH_PROCESS_NODE)mouseEvent->Node;

            if (GetKeyState(VK_CONTROL) >= 0)
            {
                PhDeselectAllProcessNodes();
            }

            if (!node)
                break;

            if (ProcessTreeListSortOrder == NoSortOrder)
            {
                // in NoSortOrder we select subtree (TheEragon)

                // init last index to self, this way we select only the process if there are no children (TheEragon)
                ULONG lastChildIndex = mouseEvent->Node->Index;
                if (node->Children->Count)
                {
                    // get index of last child
                    PPH_PROCESS_NODE lastChild = (PPH_PROCESS_NODE)node->Children->Items[node->Children->Count - 1];
                    lastChildIndex = lastChild->Node.Index;
                }

                TreeNew_SelectRange(ProcessTreeListHandle, mouseEvent->Node->Index, lastChildIndex);
            }
            else
            {
                // in sorted order we select all processes with same name (TheEragon)

                ULONG first = ULONG_MAX;
                ULONG last = 0;

                for (ULONG i = 0; i < ProcessNodeList->Count; i++)
                {
                    PPH_PROCESS_NODE item = ProcessNodeList->Items[i];

                    if (PhEqualString(node->ProcessItem->ProcessName, item->ProcessItem->ProcessName, TRUE))
                    {
                        if (first > item->Node.Index)
                            first = item->Node.Index;

                        if (last < item->Node.Index)
                            last = item->Node.Index;
                    }
                    else if (first != ULONG_MAX)
                    {
                        // select found range and reset indexes (TheEragon)
                        TreeNew_SelectRange(ProcessTreeListHandle, first, last);

                        first = ULONG_MAX;
                        last = 0;
                    }
                }

                // select last found range, if any (TheEragon)
                if (first != ULONG_MAX)
                    TreeNew_SelectRange(ProcessTreeListHandle, first, last);
            }
        }
        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;
}

_Success_(return)
BOOLEAN PhGetSelectedProcessItems(
    _Out_ PPH_PROCESS_ITEM **Processes,
    _Out_ PULONG NumberOfProcesses
    )
{
    PH_ARRAY array;
    ULONG i;

    PhInitializeArray(&array, sizeof(PVOID), 2);

    for (i = 0; i < ProcessNodeList->Count; i++)
    {
        PPH_PROCESS_NODE node = ProcessNodeList->Items[i];

        if (node->Node.Visible && node->Node.Selected)
        {
            PhAddItemArray(&array, &node->ProcessItem);
        }
    }

    if (PhFinalArrayCount(&array))
    {
        *NumberOfProcesses = (ULONG)PhFinalArrayCount(&array);
        *Processes = PhFinalArrayItems(&array);
        return TRUE;
    }
    else
    {
        *NumberOfProcesses = 0;
        *Processes = NULL;
        PhDeleteArray(&array);
        return FALSE;
    }
}

PPH_PROCESS_NODE PhGetSelectedProcessNode(
    VOID
    )
{
    PPH_PROCESS_NODE processNode = NULL;
    ULONG i;

    for (i = 0; i < ProcessNodeList->Count; i++)
    {
        PPH_PROCESS_NODE node = ProcessNodeList->Items[i];

        if (node->Node.Selected)
        {
            processNode = node;
            break;
        }
    }

    return processNode;
}

VOID PhGetSelectedProcessNodes(
    _Out_ PPH_PROCESS_NODE **Nodes,
    _Out_ PULONG NumberOfNodes
    )
{
    PH_ARRAY array;
    ULONG i;

    PhInitializeArray(&array, sizeof(PVOID), 2);

    for (i = 0; i < ProcessNodeList->Count; i++)
    {
        PPH_PROCESS_NODE node = ProcessNodeList->Items[i];

        if (node->Node.Visible && node->Node.Selected)
            PhAddItemArray(&array, &node);
    }

    *NumberOfNodes = (ULONG)PhFinalArrayCount(&array);
    *Nodes = PhFinalArrayItems(&array);
}

BOOLEAN PhGetProcessItemServices(
    _In_ PPH_PROCESS_ITEM ProcessItem,
    _Out_ PPH_SERVICE_ITEM** Services,
    _Out_ PULONG NumberOfServices
    )
{
    PH_ARRAY array;
    ULONG enumerationKey = 0;
    PPH_SERVICE_ITEM serviceItem;

    if (!(ProcessItem->ServiceList && ProcessItem->ServiceList->Count != 0))
        return FALSE;

    PhInitializeArray(&array, sizeof(PVOID), 2);

    PhAcquireQueuedLockShared(&ProcessItem->ServiceListLock);

    while (PhEnumPointerList(
        ProcessItem->ServiceList,
        &enumerationKey,
        &serviceItem
        ))
    {
        PhReferenceObject(serviceItem);
        PhAddItemArray(&array, &serviceItem);
    }

    PhReleaseQueuedLockShared(&ProcessItem->ServiceListLock);

    *NumberOfServices = (ULONG)PhFinalArrayCount(&array);
    *Services = PhFinalArrayItems(&array);
    return TRUE;
}

static VOID PhpAddAndPropagateProcessItems(
    _In_ PPH_ARRAY ProcessesArray,
    _In_ PPH_PROCESS_NODE ProcessNode
    )
{
    for (ULONG i = 0; i < ProcessNode->Children->Count; i++)
    {
        PPH_PROCESS_NODE child = ProcessNode->Children->Items[i];

        if (child->Children)
        {
            PhpAddAndPropagateProcessItems(ProcessesArray, child);
        }
    }

    PhAddItemArray(ProcessesArray, &ProcessNode->ProcessItem);
}

VOID PhGetSelectedAndPropagateProcessItems(
    _Out_ PPH_PROCESS_ITEM **Processes,
    _Out_ PULONG NumberOfProcesses
    )
{
    PH_ARRAY array;
    ULONG i;

    PhInitializeArray(&array, sizeof(PVOID), 2);

    for (i = 0; i < ProcessNodeList->Count; i++)
    {
        PPH_PROCESS_NODE node = ProcessNodeList->Items[i];

        if (node->Node.Visible && node->Node.Selected)
        {
            if (PhCsPropagateCpuUsage && !node->Node.Expanded)
            {
                PhpAddAndPropagateProcessItems(&array, node);
            }
            else
            {
                PhAddItemArray(&array, &node->ProcessItem);
            }
        }
    }

    *NumberOfProcesses = (ULONG)PhFinalArrayCount(&array);
    *Processes = PhFinalArrayItems(&array);
}

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_FocusMarkSelectNode(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)
        {
            if (getCellText.Text.Length == 0)
                text = PhReferenceEmptyString();
            else
                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 + UInt32x32To64(Level, 2) * sizeof(WCHAR)
                );
            wmemset(text->Buffer, L' ', UInt32x32To64(Level, 2));
            memcpy(&text->Buffer[UInt32x32To64(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 management 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;
}

/**
* Determines if the process item should show the image coherency in the UI.
*
* \param[in] ProcessItem - Process item to check.
* \param[in] CheckThreshold - If TRUE the image low coherency threshold is
* checked, see: LowImageCoherencyThreshold.
*
* \return TRUE if the image coherency should be shown, FALSE otherwise.
*/
BOOLEAN PhpShouldShowImageCoherency(
    _In_ PPH_PROCESS_ITEM ProcessItem,
    _In_ BOOLEAN CheckThreshold
    )
{
    const FLOAT LowImageCoherencyThreshold = 0.5f; /**< Limit for displaying "low image coherency" */

    if (PhCsImageCoherencyScanLevel == 0)
    {
        //
        // The advanced option for the image coherency scan level is 0.
        // This disables the scanning and we should not show the image
        // coherency.
        //
        return FALSE;
    }

    //
    // If we haven't done the image coherency check yet, don't show. We
    // initialize the process item with STATUS_PENDING to denote this.
    // See: PhCreateProcessItem
    //
    if (ProcessItem->ImageCoherencyStatus == STATUS_PENDING)
    {
        return FALSE;
    }

    //
    // Exclude the fake processes and system idle PID
    //
    if (!PH_IS_REAL_PROCESS_ID(ProcessItem->ProcessId))
    {
        return FALSE;
    }

    //
    // Do not show if the process has no image file name (Secure System)
    //
    if (PhIsNullOrEmptyString(ProcessItem->FileName))
    {
        return FALSE;
    }

    //
    // We don't do the coherency check if the created process is WSL
    //
    if (ProcessItem->IsSubsystemProcess)
    {
        return FALSE;
    }

    if (NT_SUCCESS(ProcessItem->ImageCoherencyStatus) ||
        (ProcessItem->ImageCoherencyStatus == STATUS_INVALID_IMAGE_NOT_MZ) ||
        (ProcessItem->ImageCoherencyStatus == STATUS_INVALID_IMAGE_FORMAT) ||
        (ProcessItem->ImageCoherencyStatus == STATUS_INVALID_IMAGE_HASH) ||
        (ProcessItem->ImageCoherencyStatus == STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT))
    {
        //
        // The coherency status is a success code or a known "valid" error
        // from the coherency calculation (PhGetProcessImageCoherency).
        // We should show the coherency value.
        //

        if (CheckThreshold)
        {
            //
            // A threshold check is requested. This is generally used for
            // checking if we should highlight an entry. If the coherency
            // is below a given threshold return true. Otherwise false.
            //
            if (ProcessItem->ImageCoherency <= LowImageCoherencyThreshold)
            {
                return TRUE;
            }
            else
            {
                return FALSE;
            }

        }

        //
        // No special handling, return true.
        //
        return TRUE;
    }

    //
    // Any other error NTSTATUS we don't show the coherency value, we'll
    // show the reason why we failed the calculation (a string representation
    // of the status code).
    //

    return FALSE;
}
