/*
 * Process Hacker ToolStatus -
 *   main program
 *
 * Copyright (C) 2011-2016 dmex
 * Copyright (C) 2010-2016 wj32
 *
 * This file is part of Process Hacker.
 *
 * Process Hacker is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Process Hacker is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Process Hacker.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "toolstatus.h"

PPH_STRING GetSearchboxText(
    VOID
    );

VOID RegisterTabSearch(
    _In_ INT TabIndex,
    _In_ PWSTR BannerText
    );

PTOOLSTATUS_TAB_INFO RegisterTabInfo(
    _In_ INT TabIndex
    );

TOOLSTATUS_CONFIG ToolStatusConfig = { 0 };
HWND ProcessTreeNewHandle = NULL;
HWND ServiceTreeNewHandle = NULL;
HWND NetworkTreeNewHandle = NULL;
INT SelectedTabIndex;
BOOLEAN UpdateAutomatically = TRUE;
BOOLEAN UpdateGraphs = TRUE;
TOOLBAR_THEME ToolBarTheme = TOOLBAR_THEME_NONE;
TOOLBAR_DISPLAY_STYLE DisplayStyle = TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT;
SEARCHBOX_DISPLAY_MODE SearchBoxDisplayMode = SEARCHBOX_DISPLAY_MODE_ALWAYSSHOW;
REBAR_DISPLAY_LOCATION RebarDisplayLocation = REBAR_DISPLAY_LOCATION_TOP;
HWND RebarHandle = NULL;
HWND ToolBarHandle = NULL;
HWND SearchboxHandle = NULL;
HMENU MainMenu = NULL;
HACCEL AcceleratorTable = NULL;
PPH_STRING SearchboxText = NULL;
PH_PLUGIN_SYSTEM_STATISTICS SystemStatistics = { 0 };
PH_CALLBACK_DECLARE(SearchChangedEvent);
PPH_HASHTABLE TabInfoHashtable;
PPH_TN_FILTER_ENTRY ProcessTreeFilterEntry = NULL;
PPH_TN_FILTER_ENTRY ServiceTreeFilterEntry = NULL;
PPH_TN_FILTER_ENTRY NetworkTreeFilterEntry = NULL;
PPH_PLUGIN PluginInstance = NULL;
TOOLSTATUS_INTERFACE PluginInterface =
{
    TOOLSTATUS_INTERFACE_VERSION,
    GetSearchboxText,
    WordMatchStringRef,
    RegisterTabSearch,
    &SearchChangedEvent,
    RegisterTabInfo
};

static ULONG TargetingMode = 0;
static BOOLEAN TargetingWindow = FALSE;
static BOOLEAN TargetingCurrentWindowDraw = FALSE;
static BOOLEAN TargetingCompleted = FALSE;
static HWND TargetingCurrentWindow = NULL;
static PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration;
static PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration;
static PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration;
static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration;
static PH_CALLBACK_REGISTRATION LayoutPaddingCallbackRegistration;
static PH_CALLBACK_REGISTRATION TabPageCallbackRegistration;
static PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration;
static PH_CALLBACK_REGISTRATION ServiceTreeNewInitializingCallbackRegistration;
static PH_CALLBACK_REGISTRATION NetworkTreeNewInitializingCallbackRegistration;

PPH_STRING GetSearchboxText(
    VOID
    )
{
    return SearchboxText;
}

VOID NTAPI ProcessesUpdatedCallback(
    _In_opt_ PVOID Parameter,
    _In_opt_ PVOID Context
    )
{
    ProcessesUpdatedCount++;

    if (ProcessesUpdatedCount < 2)
        return;

    PhPluginGetSystemStatistics(&SystemStatistics);

    if (UpdateGraphs)
        ToolbarUpdateGraphs();

    if (ToolStatusConfig.StatusBarEnabled)
        StatusBarUpdate(FALSE);
}

VOID NTAPI TreeNewInitializingCallback(
    _In_opt_ PVOID Parameter,
    _In_opt_ PVOID Context
    )
{
    *(HWND *)Context = ((PPH_PLUGIN_TREENEW_INFORMATION)Parameter)->TreeNewHandle;
}

VOID RegisterTabSearch(
    _In_ INT TabIndex,
    _In_ PWSTR BannerText
    )
{
    PTOOLSTATUS_TAB_INFO tabInfo;

    tabInfo = RegisterTabInfo(TabIndex);
    tabInfo->BannerText = BannerText;
}

PTOOLSTATUS_TAB_INFO RegisterTabInfo(
    _In_ INT TabIndex
    )
{
    PTOOLSTATUS_TAB_INFO tabInfoCopy;
    PVOID *entry;

    tabInfoCopy = PhCreateAlloc(sizeof(TOOLSTATUS_TAB_INFO));
    memset(tabInfoCopy, 0, sizeof(TOOLSTATUS_TAB_INFO));

    if (!PhAddItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex), tabInfoCopy))
    {
        PhClearReference(&tabInfoCopy);

        if (entry = PhFindItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex)))
            tabInfoCopy = *entry;
    }

    return tabInfoCopy;
}

PTOOLSTATUS_TAB_INFO FindTabInfo(
    _In_ INT TabIndex
    )
{
    PVOID *entry;

    if (entry = PhFindItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex)))
        return *entry;

    return NULL;
}

HWND GetCurrentTreeNewHandle(
    VOID
    )
{
    HWND treeNewHandle = NULL;

    switch (SelectedTabIndex)
    {
    case 0:
        treeNewHandle = ProcessTreeNewHandle;
        break;
    case 1:
        treeNewHandle = ServiceTreeNewHandle;
        break;
    case 2:
        treeNewHandle = NetworkTreeNewHandle;
        break;
    default:
        {
            PTOOLSTATUS_TAB_INFO tabInfo;

            if ((tabInfo = FindTabInfo(SelectedTabIndex)) && tabInfo->GetTreeNewHandle)
            {
                treeNewHandle = tabInfo->GetTreeNewHandle();
            }
        }
        break;
    }

    return treeNewHandle;
}

VOID ShowCustomizeMenu(
    VOID
    )
{
    POINT cursorPos;
    PPH_EMENU menu;
    PPH_EMENU_ITEM selectedItem;

    GetCursorPos(&cursorPos);

    menu = PhCreateEMenu();
    PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_SEARCHBOX, L"Search box", NULL, NULL), -1);
    PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_CPU_GRAPH, L"CPU history", NULL, NULL), -1);
    PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_IO_GRAPH, L"I/O history", NULL, NULL), -1);
    PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_MEMORY_GRAPH, L"Physical memory history", NULL, NULL), -1);
    PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_COMMIT_GRAPH, L"Commit charge history", NULL, NULL), -1);
    PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
    PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_TOOLBAR_LOCKUNLOCK, L"Lock the toolbar", NULL, NULL), -1);
    PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_TOOLBAR_CUSTOMIZE, L"Customize...", NULL, NULL), -1);

    if (ToolStatusConfig.SearchBoxEnabled)
    {
        PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_SEARCHBOX, PH_EMENU_CHECKED, PH_EMENU_CHECKED);
    }

    if (ToolStatusConfig.CpuGraphEnabled)
    {
        PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_CPU_GRAPH, PH_EMENU_CHECKED, PH_EMENU_CHECKED);
    }

    if (ToolStatusConfig.MemGraphEnabled)
    {
        PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_MEMORY_GRAPH, PH_EMENU_CHECKED, PH_EMENU_CHECKED);
    }

    if (ToolStatusConfig.CommitGraphEnabled)
    {
        PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_COMMIT_GRAPH, PH_EMENU_CHECKED, PH_EMENU_CHECKED);
    }

    if (ToolStatusConfig.IoGraphEnabled)
    {
        PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_IO_GRAPH, PH_EMENU_CHECKED, PH_EMENU_CHECKED);
    }

    if (ToolStatusConfig.ToolBarLocked)
    {
        PhSetFlagsEMenuItem(menu, COMMAND_ID_TOOLBAR_LOCKUNLOCK, PH_EMENU_CHECKED, PH_EMENU_CHECKED);
    }

    selectedItem = PhShowEMenu(
        menu,
        PhMainWndHandle,
        PH_EMENU_SHOW_LEFTRIGHT,
        PH_ALIGN_LEFT | PH_ALIGN_TOP,
        cursorPos.x,
        cursorPos.y
        );

    if (selectedItem && selectedItem->Id != -1)
    {
        switch (selectedItem->Id)
        {
        case COMMAND_ID_ENABLE_SEARCHBOX:
            {
                ToolStatusConfig.SearchBoxEnabled = !ToolStatusConfig.SearchBoxEnabled;

                PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags);

                ToolbarLoadSettings();
                ReBarSaveLayoutSettings();

                if (ToolStatusConfig.SearchBoxEnabled)
                {
                    // Adding the Searchbox makes it focused, 
                    // reset the focus back to the main window.
                    SetFocus(PhMainWndHandle);
                }
            }
            break;
        case COMMAND_ID_ENABLE_CPU_GRAPH:
            {
                ToolStatusConfig.CpuGraphEnabled = !ToolStatusConfig.CpuGraphEnabled;

                PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags);

                ToolbarLoadSettings();
                ReBarSaveLayoutSettings();
            }
            break;
        case COMMAND_ID_ENABLE_MEMORY_GRAPH:
            {
                ToolStatusConfig.MemGraphEnabled = !ToolStatusConfig.MemGraphEnabled;

                PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags);

                ToolbarLoadSettings();
                ReBarSaveLayoutSettings();
            }
            break;
        case COMMAND_ID_ENABLE_COMMIT_GRAPH:
            {
                ToolStatusConfig.CommitGraphEnabled = !ToolStatusConfig.CommitGraphEnabled;

                PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags);

                ToolbarLoadSettings();
                ReBarSaveLayoutSettings();
            }
            break;
        case COMMAND_ID_ENABLE_IO_GRAPH:
            {
                ToolStatusConfig.IoGraphEnabled = !ToolStatusConfig.IoGraphEnabled;

                PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags);

                ToolbarLoadSettings();
                ReBarSaveLayoutSettings();
            }
            break;
        case COMMAND_ID_TOOLBAR_LOCKUNLOCK:
            {
                UINT bandCount;
                UINT bandIndex;

                bandCount = (UINT)SendMessage(RebarHandle, RB_GETBANDCOUNT, 0, 0);

                for (bandIndex = 0; bandIndex < bandCount; bandIndex++)
                {
                    REBARBANDINFO rebarBandInfo = 
                    { 
                        REBARBANDINFO_V6_SIZE, 
                        RBBIM_STYLE 
                    };

                    SendMessage(RebarHandle, RB_GETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo);

                    if (!(rebarBandInfo.fStyle & RBBS_GRIPPERALWAYS))
                    {
                        // Removing the RBBS_NOGRIPPER style doesn't remove the gripper padding,
                        // So we toggle the RBBS_GRIPPERALWAYS style to make the Toolbar remove the padding.

                        rebarBandInfo.fStyle |= RBBS_GRIPPERALWAYS;   

                        SendMessage(RebarHandle, RB_SETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo);

                        rebarBandInfo.fStyle &= ~RBBS_GRIPPERALWAYS;
                    }

                    if (rebarBandInfo.fStyle & RBBS_NOGRIPPER)
                    {
                        rebarBandInfo.fStyle &= ~RBBS_NOGRIPPER;
                    }
                    else
                    {
                        rebarBandInfo.fStyle |= RBBS_NOGRIPPER;
                    }

                    SendMessage(RebarHandle, RB_SETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo);
                }

                ToolStatusConfig.ToolBarLocked = !ToolStatusConfig.ToolBarLocked;

                PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags);

                ToolbarLoadSettings();
            }
            break;
        case COMMAND_ID_TOOLBAR_CUSTOMIZE:
            {
                ToolBarShowCustomizeDialog();
            }
            break;
        }
    }

    PhDestroyEMenu(menu);
}

VOID NTAPI TabPageUpdatedCallback(
    _In_opt_ PVOID Parameter,
    _In_opt_ PVOID Context
    )
{
    INT tabIndex = PtrToInt(Parameter);

    SelectedTabIndex = tabIndex;

    if (!SearchboxHandle)
        return;

    switch (tabIndex)
    {
    case 0:
        Edit_SetCueBannerText(SearchboxHandle, L"Search Processes (Ctrl+K)");
        break;
    case 1:
        Edit_SetCueBannerText(SearchboxHandle, L"Search Services (Ctrl+K)");
        break;
    case 2:
        Edit_SetCueBannerText(SearchboxHandle, L"Search Network (Ctrl+K)");
        break;
    default:
        {
            PTOOLSTATUS_TAB_INFO tabInfo;

            if ((tabInfo = FindTabInfo(tabIndex)) && tabInfo->BannerText)
            {
                Edit_SetCueBannerText(SearchboxHandle, PhaConcatStrings2(tabInfo->BannerText, L" (Ctrl+K)")->Buffer);
            }
            else
            {
                // Disable the textbox if we're on an unsupported tab.
                Edit_SetCueBannerText(SearchboxHandle, L"Search disabled");
            }
        }
        break;
    }
}

VOID NTAPI LayoutPaddingCallback(
    _In_opt_ PVOID Parameter,
    _In_opt_ PVOID Context
    )
{
    PPH_LAYOUT_PADDING_DATA layoutPadding = Parameter;

    if (RebarHandle && ToolStatusConfig.ToolBarEnabled)
    {
        RECT rebarRect;
        //RECT clientRect;
        //INT x, y, cx, cy;

        SendMessage(RebarHandle, WM_SIZE, 0, 0);

        // TODO: GetClientRect with PhMainWndHandle causes crash.
        //GetClientRect(PhMainWndHandle, &clientRect);
        GetClientRect(RebarHandle, &rebarRect);

        // Adjust the PH client area and exclude the rebar width.
        layoutPadding->Padding.top += rebarRect.bottom;

        // TODO: Replace CCS_TOP with CCS_NOPARENTALIGN and use below code
        //switch (RebarDisplayLocation)
        //{
        //case RebarLocationLeft:
        //    {
        //        //x = 0;
        //        //y = 0;
        //        //cx = rebarRect.right - rebarRect.left;
        //        //cy = clientRect.bottom - clientRect.top;
        //    }
        //    break;
        //case RebarLocationTop:
        //    {
        //        //x = 0;
        //        //y = 0;
        //        //cx = clientRect.right - clientRect.left;
        //        //cy = clientRect.bottom - clientRect.top;
        //
        //        // Adjust the PH client area and exclude the rebar height.
        //        //layoutPadding->Padding.top += rebarRect.bottom;
        //    }
        //    break;
        //case RebarLocationRight:
        //    {
        //        //x = clientRect.right - (rebarRect.right - rebarRect.left);
        //        //y = 0;
        //        //cx = rebarRect.right - rebarRect.left;
        //        //cy = clientRect.bottom - clientRect.top;
        //    }
        //    break;
        //case RebarLocationBottom:
        //    {
        //        //x = 0;
        //        //y = clientRect.bottom - (rebarRect.bottom - rebarRect.top) - (StatusBarEnabled ? rebarRect.bottom + 1 : 0);
        //        //cx = clientRect.right - clientRect.left;
        //        //cy = rebarRect.bottom - rebarRect.top;
        //
        //        // Adjust the PH client area and exclude the rebar width.
        //        //layoutPadding->Padding.bottom += rebarRect.bottom;
        //    }
        //    break;
        //}
        //MoveWindow(RebarHandle, x, y, cx, cy, TRUE);

        //if (SearchBoxDisplayStyle == SearchBoxDisplayAutoHide)
        //{
        //    static BOOLEAN isSearchboxVisible = FALSE;
        //    SIZE idealWidth;
        //
        //    // Query the Toolbar ideal width
        //    SendMessage(ToolBarHandle, TB_GETIDEALSIZE, FALSE, (LPARAM)&idealWidth);
        //
        //    // Hide the Searcbox band if the window size is too small...
        //    if (rebarRect.right > idealWidth.cx)
        //    {
        //        if (isSearchboxVisible)
        //        {
        //            if (!RebarBandExists(REBAR_BAND_ID_SEARCHBOX))
        //                RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, 180, 20);
        //
        //            isSearchboxVisible = FALSE;
        //        }
        //    }
        //    else
        //    {
        //        if (!isSearchboxVisible)
        //        {
        //            if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX))
        //                RebarBandRemove(REBAR_BAND_ID_SEARCHBOX);
        //
        //            isSearchboxVisible = TRUE;
        //        }
        //    }
        //}
    }

    if (StatusBarHandle && ToolStatusConfig.StatusBarEnabled)
    {
        RECT statusBarRect;

        SendMessage(StatusBarHandle, WM_SIZE, 0, 0);

        GetClientRect(StatusBarHandle, &statusBarRect);

        // Adjust the PH client area and exclude the StatusBar width.
        layoutPadding->Padding.bottom += statusBarRect.bottom;

        //InvalidateRect(StatusBarHandle, NULL, TRUE);
    }
}

BOOLEAN NTAPI MessageLoopFilter(
    _In_ PMSG Message,
    _In_ PVOID Context
    )
{
    if (
        Message->hwnd == PhMainWndHandle ||
        IsChild(PhMainWndHandle, Message->hwnd)
        )
    {
        if (TranslateAccelerator(PhMainWndHandle, AcceleratorTable, Message))
            return TRUE;

        if (Message->message == WM_SYSCHAR && ToolStatusConfig.AutoHideMenu && !GetMenu(PhMainWndHandle))
        {
            ULONG key = (ULONG)Message->wParam;

            if (key == 'h' || key == 'v' || key == 't' || key == 'u' || key == 'e')
            {
                SetMenu(PhMainWndHandle, MainMenu);
                DrawMenuBar(PhMainWndHandle);
                SendMessage(PhMainWndHandle, WM_SYSCHAR, Message->wParam, Message->lParam);
                return TRUE;
            }
        }
    }

    return FALSE;
}

VOID DrawWindowBorderForTargeting(
    _In_ HWND hWnd
    )
{
    RECT rect;
    HDC hdc;

    GetWindowRect(hWnd, &rect);
    hdc = GetWindowDC(hWnd);

    if (hdc)
    {
        INT penWidth;
        INT oldDc;
        HPEN pen;
        HBRUSH brush;

        penWidth = GetSystemMetrics(SM_CXBORDER) * 3;
        oldDc = SaveDC(hdc);

        // Get an inversion effect.
        SetROP2(hdc, R2_NOT);

        pen = CreatePen(PS_INSIDEFRAME, penWidth, RGB(0x00, 0x00, 0x00));
        SelectPen(hdc, pen);

        brush = GetStockBrush(NULL_BRUSH);
        SelectBrush(hdc, brush);

        // Draw the rectangle.
        Rectangle(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top);

        // Cleanup.
        DeleteObject(pen);

        RestoreDC(hdc, oldDc);
        ReleaseDC(hWnd, hdc);
    }
}

LRESULT CALLBACK MainWndSubclassProc(
    _In_ HWND hWnd,
    _In_ UINT uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam,
    _In_ UINT_PTR uIdSubclass,
    _In_ ULONG_PTR dwRefData
    )
{
    switch (uMsg)
    {
    case WM_COMMAND:
        {
            switch (GET_WM_COMMAND_CMD(wParam, lParam))
            {
            case EN_CHANGE:
                {
                    PPH_STRING newSearchboxText;

                    if (GET_WM_COMMAND_HWND(wParam, lParam) != SearchboxHandle)
                        break;

                    newSearchboxText = PH_AUTO(PhGetWindowText(SearchboxHandle));

                    if (!PhEqualString(SearchboxText, newSearchboxText, FALSE))
                    {
                        // Cache the current search text for our callback.
                        PhSwapReference(&SearchboxText, newSearchboxText);

                        if (!PhIsNullOrEmptyString(SearchboxText))
                        {
                            // Expand the nodes to ensure that they will be visible to the user.
                            PhExpandAllProcessNodes(TRUE);
                            PhDeselectAllProcessNodes();
                            PhDeselectAllServiceNodes();
                        }

                        PhApplyTreeNewFilters(PhGetFilterSupportProcessTreeList());
                        PhApplyTreeNewFilters(PhGetFilterSupportServiceTreeList());
                        PhApplyTreeNewFilters(PhGetFilterSupportNetworkTreeList());

                        PhInvokeCallback(&SearchChangedEvent, SearchboxText);
                    }

                    goto DefaultWndProc;
                }
                break;
            case EN_KILLFOCUS:
                {
                    if (GET_WM_COMMAND_HWND(wParam, lParam) != SearchboxHandle)
                        break;

                    if (SearchBoxDisplayMode != SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE)
                        break;

                    if (SearchboxText->Length == 0)
                    {
                        if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX))
                            RebarBandRemove(REBAR_BAND_ID_SEARCHBOX);
                    }
                }
                break;
            }

            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
            case PHAPP_ID_ESC_EXIT:
                {
                    // If we're targeting and the user presses the Esc key, cancel the targeting.
                    // We also make sure the window doesn't get closed, by filtering out the message.
                    if (TargetingWindow)
                    {
                        TargetingWindow = FALSE;
                        ReleaseCapture();

                        goto DefaultWndProc;
                    }
                }
                break;
            case ID_SEARCH:
                {
                    // handle keybind Ctrl + K
                    if (SearchboxHandle && ToolStatusConfig.SearchBoxEnabled)
                    {
                        SetFocus(SearchboxHandle);
                        Edit_SetSel(SearchboxHandle, 0, -1);
                    }

                    goto DefaultWndProc;
                }
                break;
            case ID_SEARCH_CLEAR:
                {
                    if (SearchboxHandle && ToolStatusConfig.SearchBoxEnabled)
                    {
                        SetFocus(SearchboxHandle);
                        Static_SetText(SearchboxHandle, L"");
                    }

                    goto DefaultWndProc;
                }
                break;
            case PHAPP_ID_VIEW_ALWAYSONTOP:
                {
                    // Let Process Hacker perform the default processing.
                    DefSubclassProc(hWnd, uMsg, wParam, lParam);

                    // Query the settings.
                    BOOLEAN isAlwaysOnTopEnabled = (BOOLEAN)PhGetIntegerSetting(L"MainWindowAlwaysOnTop");

                    // Set the pressed button state.
                    SendMessage(ToolBarHandle, TB_PRESSBUTTON, (WPARAM)PHAPP_ID_VIEW_ALWAYSONTOP, (LPARAM)(MAKELONG(isAlwaysOnTopEnabled, 0)));

                    goto DefaultWndProc;
                }
                break;
            case PHAPP_ID_UPDATEINTERVAL_FAST:
            case PHAPP_ID_UPDATEINTERVAL_NORMAL:
            case PHAPP_ID_UPDATEINTERVAL_BELOWNORMAL:
            case PHAPP_ID_UPDATEINTERVAL_SLOW:
            case PHAPP_ID_UPDATEINTERVAL_VERYSLOW:
                {
                    // Let Process Hacker perform the default processing.
                    DefSubclassProc(hWnd, uMsg, wParam, lParam);

                    StatusBarUpdate(TRUE);

                    goto DefaultWndProc;
                }
                break;
            case PHAPP_ID_VIEW_UPDATEAUTOMATICALLY:
                {
                    UpdateAutomatically = !UpdateAutomatically;

                    StatusBarUpdate(TRUE);
                }
                break;
            }
        }
        break;
    case WM_NOTIFY:
        {
            LPNMHDR hdr = (LPNMHDR)lParam;

            if (RebarHandle && hdr->hwndFrom == RebarHandle)
            {
                switch (hdr->code)
                {
                case RBN_HEIGHTCHANGE:
                    {
                        // Invoke the LayoutPaddingCallback.
                        SendMessage(PhMainWndHandle, WM_SIZE, 0, 0);
                    }
                    break;
                case RBN_CHEVRONPUSHED:
                    {
                        LPNMREBARCHEVRON rebar;
                        ULONG index = 0;
                        ULONG buttonCount = 0;
                        RECT toolbarRect;
                        PPH_EMENU menu;
                        PPH_EMENU_ITEM selectedItem;

                        rebar = (LPNMREBARCHEVRON)lParam;
                        menu = PhCreateEMenu();

                        GetClientRect(ToolBarHandle, &toolbarRect);

                        buttonCount = (ULONG)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0);

                        for (index = 0; index < buttonCount; index++)
                        {
                            RECT buttonRect;
                            TBBUTTONINFO buttonInfo = 
                            { 
                                sizeof(TBBUTTONINFO),
                                TBIF_BYINDEX | TBIF_STYLE | TBIF_COMMAND | TBIF_IMAGE
                            };

                            // Get the client coordinates of the button.
                            if (SendMessage(ToolBarHandle, TB_GETITEMRECT, index, (LPARAM)&buttonRect) == -1)
                                break;

                            if (buttonRect.right <= toolbarRect.right)
                                continue;

                            // Get extended button information.
                            if (SendMessage(ToolBarHandle, TB_GETBUTTONINFO, index, (LPARAM)&buttonInfo) == -1)
                                break;

                            if (buttonInfo.fsStyle == BTNS_SEP)
                            {
                                // Add separators to menu.
                                PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
                            }
                            else
                            {
                                PPH_EMENU_ITEM menuItem;

                                if (PhGetOwnTokenAttributes().Elevated && buttonInfo.idCommand == PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES)
                                {
                                    // Don't show the 'Show Details for All Processes' button in the
                                    //  dropdown menu when we're elevated.
                                    continue;
                                }

                                // Add buttons to menu.
                                menuItem = PhCreateEMenuItem(0, buttonInfo.idCommand, ToolbarGetText(buttonInfo.idCommand), NULL, NULL);

                                menuItem->Flags |= PH_EMENU_BITMAP_OWNED;
                                menuItem->Bitmap = ToolbarGetImage(buttonInfo.idCommand);

                                switch (buttonInfo.idCommand)
                                {
                                case PHAPP_ID_VIEW_ALWAYSONTOP:
                                    {
                                        // Set the pressed state.
                                        if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop"))
                                            menuItem->Flags |= PH_EMENU_CHECKED;
                                    }
                                    break;
                                case TIDC_FINDWINDOW:
                                case TIDC_FINDWINDOWTHREAD:
                                case TIDC_FINDWINDOWKILL:
                                    {
                                        // Note: These buttons are incompatible with menus.
                                        menuItem->Flags |= PH_EMENU_DISABLED;
                                    }
                                    break;
                                case TIDC_POWERMENUDROPDOWN:
                                    {
                                        // Create the sub-menu...
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOCK, L"&Lock", NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOGOFF, L"Log o&ff", NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SLEEP, L"&Sleep", NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_HIBERNATE, L"&Hibernate", NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTART, L"R&estart", NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTARTBOOTOPTIONS, L"Restart to boot &options", NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWN, L"Shu&t down", NULL, NULL), -1);
                                        PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWNHYBRID, L"H&ybrid shut down", NULL, NULL), -1);
                                    }
                                    break;
                                }

                                PhInsertEMenuItem(menu, menuItem, -1);
                            }
                        }

                        MapWindowPoints(RebarHandle, NULL, (LPPOINT)&rebar->rc, 2);

                        selectedItem = PhShowEMenu(
                            menu,
                            hWnd,
                            PH_EMENU_SHOW_LEFTRIGHT,
                            PH_ALIGN_LEFT | PH_ALIGN_TOP,
                            rebar->rc.left,
                            rebar->rc.bottom
                            );

                        if (selectedItem && selectedItem->Id != -1)
                        {
                            SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0);
                        }

                        PhDestroyEMenu(menu);
                    }
                    break;
                case RBN_LAYOUTCHANGED:
                    {
                        ReBarSaveLayoutSettings();
                    }
                    break;
                }

                goto DefaultWndProc;
            }
            else if (ToolBarHandle && hdr->hwndFrom == ToolBarHandle)
            {
                switch (hdr->code)
                {
                case TBN_GETDISPINFO:
                    {
                        LPNMTBDISPINFO toolbarDisplayInfo = (LPNMTBDISPINFO)lParam;

                        if (toolbarDisplayInfo->dwMask & TBNF_IMAGE)
                        {
                            BOOLEAN found = FALSE;

                            // Try to find the cached bitmap index.
                            // NOTE: The TBNF_DI_SETITEM flag below will cache the index so we only get called once.
                            //       However, when adding buttons from the customize dialog we get called a second time,
                            //       so we cache the index in our ToolbarButtons array to prevent ToolBarImageList from growing.
                            for (INT i = 0; i < ARRAYSIZE(ToolbarButtons); i++)
                            {
                                if (ToolbarButtons[i].idCommand == toolbarDisplayInfo->idCommand)
                                {
                                    if (ToolbarButtons[i].iBitmap != I_IMAGECALLBACK)
                                    {
                                        found = TRUE;

                                        // Cache the bitmap index.
                                        toolbarDisplayInfo->dwMask |= TBNF_DI_SETITEM;
                                        // Set the bitmap index.
                                        toolbarDisplayInfo->iImage = ToolbarButtons[i].iBitmap;
                                    }
                                    break;
                                }
                            }

                            if (!found)
                            {
                                // We didn't find a cached bitmap index... 
                                // Load the button bitmap and cache the index.
                                for (INT i = 0; i < ARRAYSIZE(ToolbarButtons); i++)
                                {
                                    if (ToolbarButtons[i].idCommand == toolbarDisplayInfo->idCommand)
                                    {
                                        HBITMAP buttonImage;
                                        
                                        buttonImage = ToolbarGetImage(toolbarDisplayInfo->idCommand);

                                        // Cache the bitmap index.
                                        toolbarDisplayInfo->dwMask |= TBNF_DI_SETITEM;
                                        // Add the image, cache the value in the ToolbarButtons array, set the bitmap index.
                                        toolbarDisplayInfo->iImage = ToolbarButtons[i].iBitmap = ImageList_Add(
                                            ToolBarImageList,
                                            buttonImage, 
                                            NULL
                                            );

                                        DeleteObject(buttonImage);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    break;
                case TBN_DROPDOWN:
                    {
                        LPNMTOOLBAR toolbar = (LPNMTOOLBAR)hdr;
                        PPH_EMENU menu;
                        PPH_EMENU_ITEM selectedItem;

                        if (toolbar->iItem != TIDC_POWERMENUDROPDOWN)
                            break;

                        menu = PhCreateEMenu();
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOCK, L"&Lock", NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOGOFF, L"Log o&ff", NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SLEEP, L"&Sleep", NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_HIBERNATE, L"&Hibernate", NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTART, L"R&estart", NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTARTBOOTOPTIONS, L"Restart to boot &options", NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWN, L"Shu&t down", NULL, NULL), -1);
                        PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWNHYBRID, L"H&ybrid shut down", NULL, NULL), -1);

                        MapWindowPoints(ToolBarHandle, NULL, (LPPOINT)&toolbar->rcButton, 2);

                        selectedItem = PhShowEMenu(
                            menu,
                            hWnd,
                            PH_EMENU_SHOW_LEFTRIGHT,
                            PH_ALIGN_LEFT | PH_ALIGN_TOP,
                            toolbar->rcButton.left,
                            toolbar->rcButton.bottom
                            );

                        if (selectedItem && selectedItem->Id != -1)
                        {
                            SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0);
                        }

                        PhDestroyEMenu(menu);
                    }
                    return TBDDRET_DEFAULT;
                case NM_LDOWN:
                    {
                        LPNMCLICK toolbar = (LPNMCLICK)hdr;
                        ULONG id = (ULONG)toolbar->dwItemSpec;

                        if (id == -1)
                            break;

                        if (id == TIDC_FINDWINDOW || id == TIDC_FINDWINDOWTHREAD || id == TIDC_FINDWINDOWKILL)
                        {
                            // Direct all mouse events to this window.
                            SetCapture(hWnd);

                            // Set the cursor.
                            SetCursor(LoadCursor(NULL, IDC_CROSS));

                            // Send the window to the bottom.
                            SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

                            TargetingWindow = TRUE;
                            TargetingCurrentWindow = NULL;
                            TargetingCurrentWindowDraw = FALSE;
                            TargetingCompleted = FALSE;
                            TargetingMode = id;

                            SendMessage(hWnd, WM_MOUSEMOVE, 0, 0);
                        }
                    }
                    break;
                case NM_RCLICK:
                    {
                        ShowCustomizeMenu();
                    }
                    break;
                }

                goto DefaultWndProc;
            }
            else if (StatusBarHandle && hdr->hwndFrom == StatusBarHandle)
            {
                switch (hdr->code)
                {
                case NM_RCLICK:
                    {
                        StatusBarShowMenu();
                    }
                    break;
                }

                goto DefaultWndProc;
            }
            else if (
                CpuGraphHandle && hdr->hwndFrom == CpuGraphHandle || 
                MemGraphHandle && hdr->hwndFrom == MemGraphHandle || 
                CommitGraphHandle && hdr->hwndFrom == CommitGraphHandle ||
                IoGraphHandle && hdr->hwndFrom == IoGraphHandle
                )
            {
                ToolbarUpdateGraphsInfo(hdr);

                goto DefaultWndProc;
            }
        }
        break;
    case WM_MOUSEMOVE:
        {
            if (TargetingWindow)
            {
                POINT cursorPos;
                HWND windowOverMouse;
                ULONG processId;
                ULONG threadId;

                GetCursorPos(&cursorPos);
                windowOverMouse = WindowFromPoint(cursorPos);

                if (TargetingCurrentWindow != windowOverMouse)
                {
                    if (TargetingCurrentWindow && TargetingCurrentWindowDraw)
                    {
                        // Invert the old border (to remove it).
                        DrawWindowBorderForTargeting(TargetingCurrentWindow);
                    }

                    if (windowOverMouse)
                    {
                        threadId = GetWindowThreadProcessId(windowOverMouse, &processId);

                        // Draw a rectangle over the current window (but not if it's one of our own).
                        if (UlongToHandle(processId) != NtCurrentProcessId())
                        {
                            DrawWindowBorderForTargeting(windowOverMouse);
                            TargetingCurrentWindowDraw = TRUE;
                        }
                        else
                        {
                            TargetingCurrentWindowDraw = FALSE;
                        }
                    }

                    TargetingCurrentWindow = windowOverMouse;
                }

                goto DefaultWndProc;
            }
        }
        break;
    case WM_LBUTTONUP:
        {
            if (TargetingWindow)
            {
                ULONG processId;
                ULONG threadId;

                TargetingCompleted = TRUE;

                // Reset the original cursor.
                SetCursor(LoadCursor(NULL, IDC_ARROW));

                // Bring the window back to the top, and preserve the Always on Top setting.
                SetWindowPos(PhMainWndHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP,
                    0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

                TargetingWindow = FALSE;
                ReleaseCapture();

                if (TargetingCurrentWindow)
                {
                    if (TargetingCurrentWindowDraw)
                    {
                        // Remove the border on the window we found.
                        DrawWindowBorderForTargeting(TargetingCurrentWindow);
                    }

                    if (ToolStatusConfig.ResolveGhostWindows)
                    {
                        // This is an undocumented function exported by user32.dll that
                        // retrieves the hung window represented by a ghost window.
                        static HWND (WINAPI *HungWindowFromGhostWindow_I)(
                            _In_ HWND hWnd
                            );

                        if (!HungWindowFromGhostWindow_I)
                            HungWindowFromGhostWindow_I = PhGetModuleProcAddress(L"user32.dll", "HungWindowFromGhostWindow");

                        if (HungWindowFromGhostWindow_I)
                        {
                            HWND hungWindow = HungWindowFromGhostWindow_I(TargetingCurrentWindow);

                            // The call will have failed if the window wasn't actually a ghost
                            // window.
                            if (hungWindow)
                                TargetingCurrentWindow = hungWindow;
                        }
                    }

                    threadId = GetWindowThreadProcessId(TargetingCurrentWindow, &processId);

                    if (threadId && processId && UlongToHandle(processId) != NtCurrentProcessId())
                    {
                        PPH_PROCESS_NODE processNode;

                        processNode = PhFindProcessNode(UlongToHandle(processId));

                        if (processNode)
                        {
                            ProcessHacker_SelectTabPage(hWnd, 0);
                            ProcessHacker_SelectProcessNode(hWnd, processNode);
                        }

                        switch (TargetingMode)
                        {
                        case TIDC_FINDWINDOWTHREAD:
                            {
                                PPH_PROCESS_PROPCONTEXT propContext;
                                PPH_PROCESS_ITEM processItem;

                                if (processItem = PhReferenceProcessItem(UlongToHandle(processId)))
                                {
                                    if (propContext = PhCreateProcessPropContext(hWnd, processItem))
                                    {
                                        PhSetSelectThreadIdProcessPropContext(propContext, UlongToHandle(threadId));
                                        PhShowProcessProperties(propContext);
                                        PhDereferenceObject(propContext);
                                    }

                                    PhDereferenceObject(processItem);
                                }
                                else
                                {
                                    PhShowError(hWnd, L"The process (PID %lu) does not exist.", processId);
                                }
                            }
                            break;
                        case TIDC_FINDWINDOWKILL:
                            {
                                PPH_PROCESS_ITEM processItem;

                                if (processItem = PhReferenceProcessItem(UlongToHandle(processId)))
                                {
                                    PhUiTerminateProcesses(hWnd, &processItem, 1);
                                    PhDereferenceObject(processItem);
                                }
                                else
                                {
                                    PhShowError(hWnd, L"The process (PID %lu) does not exist.", processId);
                                }
                            }
                            break;
                        }
                    }
                }

                goto DefaultWndProc;
            }
        }
        break;
    case WM_CAPTURECHANGED:
        {
            if (!TargetingCompleted)
            {
                // The user cancelled the targeting, probably by pressing the Esc key.

                // Remove the border on the currently selected window.
                if (TargetingCurrentWindow)
                {
                    if (TargetingCurrentWindowDraw)
                    {
                        // Remove the border on the window we found.
                        DrawWindowBorderForTargeting(TargetingCurrentWindow);
                    }
                }

                SetWindowPos(PhMainWndHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP,
                    0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

                TargetingCompleted = TRUE;
            }
        }
        break;
    case WM_SIZE:
        // Resize PH main window client-area.
        ProcessHacker_InvalidateLayoutPadding(hWnd);
        break;
    case WM_SETTINGCHANGE:
        // Forward to the Searchbox so we can reinitialize the settings...
        SendMessage(SearchboxHandle, WM_SETTINGCHANGE, 0, 0);
        break;
    case WM_SHOWWINDOW:
        {
            UpdateGraphs = (BOOLEAN)wParam;
        }
        break;
    case WM_SYSCOMMAND:
        {
            if ((wParam & 0xFFF0) == SC_KEYMENU && lParam == 0)
            {
                if (!ToolStatusConfig.AutoHideMenu)
                    break;

                if (GetMenu(PhMainWndHandle))
                {
                    SetMenu(PhMainWndHandle, NULL);
                }
                else
                {
                    SetMenu(PhMainWndHandle, MainMenu);
                    DrawMenuBar(PhMainWndHandle);
                }
            }
            else if ((wParam & 0xFFF0) == SC_MINIMIZE)
            {
                UpdateGraphs = FALSE;
            }
            else if ((wParam & 0xFFF0) == SC_RESTORE)
            {
                UpdateGraphs = TRUE;
            }
        }
        break;
    case WM_EXITMENULOOP:
        {
            if (!ToolStatusConfig.AutoHideMenu)
                break;

            if (GetMenu(PhMainWndHandle))
            {
                SetMenu(PhMainWndHandle, NULL);
            }
        }
        break;
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);

DefaultWndProc:
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

VOID NTAPI MainWindowShowingCallback(
    _In_opt_ PVOID Parameter,
    _In_opt_ PVOID Context
    )
{
    PhRegisterMessageLoopFilter(MessageLoopFilter, NULL);
    PhRegisterCallback(
        ProcessHacker_GetCallbackLayoutPadding(PhMainWndHandle),
        LayoutPaddingCallback,
        NULL,
        &LayoutPaddingCallbackRegistration
        );
    SetWindowSubclass(PhMainWndHandle, MainWndSubclassProc, 0, 0);

    ToolbarLoadSettings();
    ReBarLoadLayoutSettings();
    StatusBarLoadSettings();

    MainMenu = GetMenu(PhMainWndHandle);
    if (ToolStatusConfig.AutoHideMenu)
    {
        SetMenu(PhMainWndHandle, NULL);
    }
}

VOID NTAPI LoadCallback(
    _In_opt_ PVOID Parameter,
    _In_opt_ PVOID Context
    )
{
    ToolStatusConfig.Flags = PhGetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG);
    ToolBarTheme = (TOOLBAR_THEME)PhGetIntegerSetting(SETTING_NAME_TOOLBAR_THEME);
    DisplayStyle = (TOOLBAR_DISPLAY_STYLE)PhGetIntegerSetting(SETTING_NAME_TOOLBARDISPLAYSTYLE);
    SearchBoxDisplayMode = (SEARCHBOX_DISPLAY_MODE)PhGetIntegerSetting(SETTING_NAME_SEARCHBOXDISPLAYMODE);
    UpdateGraphs = !PhGetIntegerSetting(L"StartHidden");
}

VOID NTAPI ShowOptionsCallback(
    _In_opt_ PVOID Parameter,
    _In_opt_ PVOID Context
    )
{
    ShowOptionsDialog(Parameter);
}

LOGICAL DllMain(
    _In_ HINSTANCE Instance,
    _In_ ULONG Reason,
    _Reserved_ PVOID Reserved
    )
{
    switch (Reason)
    {
    case DLL_PROCESS_ATTACH:
        {
            PPH_PLUGIN_INFORMATION info;
            PH_SETTING_CREATE settings[] =
            {
                { IntegerSettingType, SETTING_NAME_TOOLSTATUS_CONFIG, L"1F" },
                { IntegerSettingType, SETTING_NAME_TOOLBAR_THEME, L"0" },
                { IntegerSettingType, SETTING_NAME_TOOLBARDISPLAYSTYLE, L"1" },
                { IntegerSettingType, SETTING_NAME_SEARCHBOXDISPLAYMODE, L"0" },
                { StringSettingType, SETTING_NAME_REBAR_CONFIG, L"" },
                { StringSettingType, SETTING_NAME_TOOLBAR_CONFIG, L"" },
                { StringSettingType, SETTING_NAME_STATUSBAR_CONFIG, L"" }
            };

            PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info);

            if (!PluginInstance)
                return FALSE;

            info->DisplayName = L"Toolbar and Status Bar";
            info->Author = L"dmex, wj32";
            info->Description = L"Adds a Toolbar, Status Bar and Search box.\r\n\r\nModern Toolbar icons by http://www.icons8.com";
            info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1119";
            info->HasOptions = TRUE;
            info->Interface = &PluginInterface;

            PhRegisterCallback(
                PhGetPluginCallback(PluginInstance, PluginCallbackLoad),
                LoadCallback,
                NULL,
                &PluginLoadCallbackRegistration
                );
            PhRegisterCallback(
                PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions),
                ShowOptionsCallback,
                NULL,
                &PluginShowOptionsCallbackRegistration
                );
            PhRegisterCallback(
                PhGetGeneralCallback(GeneralCallbackMainWindowShowing),
                MainWindowShowingCallback,
                NULL,
                &MainWindowShowingCallbackRegistration
                );
            PhRegisterCallback(
                PhGetGeneralCallback(GeneralCallbackProcessesUpdated),
                ProcessesUpdatedCallback,
                NULL,
                &ProcessesUpdatedCallbackRegistration
                );
            PhRegisterCallback(
                PhGetGeneralCallback(GeneralCallbackMainWindowTabChanged),
                TabPageUpdatedCallback,
                NULL,
                &TabPageCallbackRegistration
                );
            PhRegisterCallback(
                PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing),
                TreeNewInitializingCallback,
                &ProcessTreeNewHandle,
                &ProcessTreeNewInitializingCallbackRegistration
                );
            PhRegisterCallback(
                PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing),
                TreeNewInitializingCallback,
                &ServiceTreeNewHandle,
                &ServiceTreeNewInitializingCallbackRegistration
                );
            PhRegisterCallback(
                PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing),
                TreeNewInitializingCallback,
                &NetworkTreeNewHandle,
                &NetworkTreeNewInitializingCallbackRegistration
                );

            PhAddSettings(settings, ARRAYSIZE(settings));

            AcceleratorTable = LoadAccelerators(
                Instance,
                MAKEINTRESOURCE(IDR_MAINWND_ACCEL)
                );

            TabInfoHashtable = PhCreateSimpleHashtable(3);
        }
        break;
    }

    return TRUE;
}