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