/* * Process Hacker Extra Plugins - * Plugin Manager * * Copyright (C) 2016 dmex * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Process Hacker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . */ #include "main.h" BOOLEAN WordMatchStringRef( _In_ PWCT_CONTEXT Context, _In_ PPH_STRINGREF Text ) { PH_STRINGREF part; PH_STRINGREF remainingPart; remainingPart = Context->SearchboxText->sr; while (remainingPart.Length) { PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); if (part.Length) { if (PhFindStringInStringRef(Text, &part, TRUE) != -1) return TRUE; } } return FALSE; } BOOLEAN WordMatchStringZ( _In_ PWCT_CONTEXT Context, _In_ PWSTR Text ) { PH_STRINGREF text; PhInitializeStringRef(&text, Text); return WordMatchStringRef(Context, &text); } VOID UpdateTreeView( _In_ PWCT_CONTEXT Context ) { //PhSwapReference(&Context->TreeText, PhCreateString(L"Loading Plugins...")); //TreeNew_SetEmptyText(Context->TreeNewHandle, &Context->TreeText->sr, 0); PhApplyTreeNewFilters(Context->TreeFilter); TreeNew_AutoSizeColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); } VOID UpdateMenuView(_In_ PWCT_CONTEXT Context, UINT Id) { HWND active = Context->PluginMenuActive; Context->PluginMenuActiveId = Id; Context->PluginMenuActive = GetDlgItem(Context->DialogHandle, Id); RedrawWindow(active, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE | RDW_FRAME); } LRESULT DrawButton( _In_ PWCT_CONTEXT Context, _In_ LPARAM lParam ) { LPNMTVCUSTOMDRAW drawInfo = (LPNMTVCUSTOMDRAW)lParam; BOOLEAN isGrayed = (drawInfo->nmcd.uItemState & CDIS_GRAYED) == CDIS_GRAYED; BOOLEAN isChecked = (drawInfo->nmcd.uItemState & CDIS_CHECKED) == CDIS_CHECKED; BOOLEAN isDisabled = (drawInfo->nmcd.uItemState & CDIS_DISABLED) == CDIS_DISABLED; BOOLEAN isSelected = (drawInfo->nmcd.uItemState & CDIS_SELECTED) == CDIS_SELECTED; BOOLEAN isHighlighted = (drawInfo->nmcd.uItemState & CDIS_HOT) == CDIS_HOT; BOOLEAN isFocused = (drawInfo->nmcd.uItemState & CDIS_FOCUS) == CDIS_FOCUS; if (drawInfo->nmcd.dwDrawStage == CDDS_PREPAINT) { HPEN PenActive = CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); HPEN PenNormal = CreatePen(PS_SOLID, 1, RGB(0, 0xff, 0)); HBRUSH BrushNormal = GetSysColorBrush(COLOR_3DFACE); HBRUSH BrushSelected = CreateSolidBrush(RGB(0xff, 0xff, 0xff)); HBRUSH BrushPushed = CreateSolidBrush(RGB(153, 209, 255)); PPH_STRING windowText = PH_AUTO(PhGetWindowText(drawInfo->nmcd.hdr.hwndFrom)); SetBkMode(drawInfo->nmcd.hdc, TRANSPARENT); if (isHighlighted || Context->PluginMenuActiveId == drawInfo->nmcd.hdr.idFrom) { FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushNormal); SelectObject(drawInfo->nmcd.hdc, PenActive); SelectObject(drawInfo->nmcd.hdc, Context->BoldFontHandle); } else if (isSelected) { FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushPushed); SelectObject(drawInfo->nmcd.hdc, PenActive); SelectObject(drawInfo->nmcd.hdc, Context->BoldFontHandle); } else { FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushNormal); } DrawText( drawInfo->nmcd.hdc, windowText->Buffer, (ULONG)windowText->Length / 2, &drawInfo->nmcd.rc, DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE ); MoveToEx(drawInfo->nmcd.hdc, drawInfo->nmcd.rc.left, drawInfo->nmcd.rc.bottom, 0); LineTo(drawInfo->nmcd.hdc, drawInfo->nmcd.rc.right, drawInfo->nmcd.rc.bottom); DeletePen(PenNormal); DeletePen(PenActive); DeleteBrush(BrushNormal); DeleteBrush(BrushSelected); DeleteBrush(BrushPushed); return CDRF_SKIPDEFAULT; } return CDRF_DODEFAULT; } BOOLEAN ProcessTreeFilterCallback( _In_ PPH_TREENEW_NODE Node, _In_opt_ PVOID Context ) { PWCT_CONTEXT context = Context; PPLUGIN_NODE node = (PPLUGIN_NODE)Node; // Hide plugins from different tabs if (node->State != context->Type) return FALSE; // Hide uninstalled plugins from the local tab //if (node->Type == PLUGIN_VIEW_LOCAL && node->State == PLUGIN_STATE_UNINSTALLED) // return FALSE; //// Hide installed plugins from the remote tab //if (node->Type == PLUGIN_VIEW_REMOTE && node->State == PLUGIN_STATE_INSTALLED) // return FALSE; if (PhIsNullOrEmptyString(context->SearchboxText)) return TRUE; if (!PhIsNullOrEmptyString(node->InternalName)) { if (WordMatchStringRef(context, &node->InternalName->sr)) return TRUE; } if (!PhIsNullOrEmptyString(node->Id)) { if (WordMatchStringRef(context, &node->Id->sr)) return TRUE; } if (!PhIsNullOrEmptyString(node->Name)) { if (WordMatchStringRef(context, &node->Name->sr)) return TRUE; } if (!PhIsNullOrEmptyString(node->Version)) { if (WordMatchStringRef(context, &node->Version->sr)) return TRUE; } if (!PhIsNullOrEmptyString(node->Author)) { if (WordMatchStringRef(context, &node->Author->sr)) return TRUE; } if (!PhIsNullOrEmptyString(node->Description)) { if (WordMatchStringRef(context, &node->Description->sr)) return TRUE; } return FALSE; } INT_PTR CALLBACK CloudPluginsDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { PWCT_CONTEXT context = NULL; if (uMsg == WM_INITDIALOG) { context = (PWCT_CONTEXT)PhAllocate(sizeof(WCT_CONTEXT)); memset(context, 0, sizeof(WCT_CONTEXT)); SetProp(hwndDlg, L"Context", (HANDLE)context); } else { context = (PWCT_CONTEXT)GetProp(hwndDlg, L"Context"); if (uMsg == WM_DESTROY) { PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg); PhDeleteLayoutManager(&context->LayoutManager); PhUnregisterDialog(hwndDlg); RemoveProp(hwndDlg, L"Context"); PhFree(context); PostQuitMessage(0); } } if (context == NULL) return FALSE; switch (uMsg) { case WM_INITDIALOG: { LOGFONT logFont; context->DialogHandle = hwndDlg; context->PluginMenuActiveId = IDC_INSTALLED; context->PluginMenuActive = GetDlgItem(hwndDlg, IDC_INSTALLED); context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_PLUGINTREE); context->SearchHandle = GetDlgItem(hwndDlg, IDC_SEARCHBOX); context->SearchboxText = PhReferenceEmptyString(); CreateSearchControl(hwndDlg, context->SearchHandle, ID_SEARCH_CLEAR); if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) { context->NormalFontHandle = CreateFont( -PhMultiplyDivideSigned(-14, PhGlobalDpi, 72), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY | ANTIALIASED_QUALITY, DEFAULT_PITCH, logFont.lfFaceName ); context->BoldFontHandle = CreateFont( -PhMultiplyDivideSigned(-16, PhGlobalDpi, 72), 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY | ANTIALIASED_QUALITY, DEFAULT_PITCH, logFont.lfFaceName ); } PhCenterWindow(hwndDlg, PhMainWndHandle); WtcInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext); PhAddTreeNewFilter(context->TreeFilter = WtcGetTreeListFilterSupport(), (PPH_TN_FILTER_FUNCTION)ProcessTreeFilterCallback, context); PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL); PhAddLayoutItem(&context->LayoutManager, context->SearchHandle, NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_CLEANUP), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_DISABLED), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); PhLoadWindowPlacementFromSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg); EnumerateLoadedPlugins(context); SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount()))); PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), QueryPluginsCallbackThread, context); UpdateTreeView(context); SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_INSTALLED), TRUE); } break; case WM_SIZE: PhLayoutManagerLayout(&context->LayoutManager); TreeNew_AutoSizeColumn(context->TreeNewHandle, TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); break; case WM_COMMAND: { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case EN_CHANGE: { PPH_STRING newSearchboxText; newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchHandle)); if (!PhEqualString(context->SearchboxText, newSearchboxText, FALSE)) { // Cache the current search text for our callback. PhSwapReference(&context->SearchboxText, newSearchboxText); if (!PhIsNullOrEmptyString(context->SearchboxText)) { // Expand the nodes to ensure that they will be visible to the user. } PhApplyTreeNewFilters(context->TreeFilter); } } break; } switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_SEARCH_CLEAR: { if (context->SearchHandle) { SetFocus(context->SearchHandle); Static_SetText(context->SearchHandle, L""); UpdateTreeView(context); } } break; case IDC_INSTALLED: { context->Type = PLUGIN_STATE_LOCAL; UpdateMenuView(context, IDC_INSTALLED); UpdateTreeView(context); } break; case IDC_BROWSE: { context->Type = PLUGIN_STATE_REMOTE; UpdateMenuView(context, IDC_BROWSE); UpdateTreeView(context); } break; case IDC_UPDATES: { context->Type = PLUGIN_STATE_UPDATE; UpdateMenuView(context, IDC_UPDATES); UpdateTreeView(context); } break; case WM_ACTION: { PPLUGIN_NODE selectedNode; if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) { if (selectedNode->State == PLUGIN_STATE_LOCAL) { if (selectedNode->PluginInstance && selectedNode->PluginInstance->Information.HasOptions) { PhInvokeCallback(PhGetPluginCallback((PPH_PLUGIN)selectedNode->PluginInstance, PluginCallbackShowOptions), hwndDlg); } } else if (selectedNode->State == PLUGIN_STATE_REMOTE) { if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL)) { } } else if (selectedNode->State == PLUGIN_STATE_UPDATE) { if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL)) { } } } } break; case ID_WCTSHOWCONTEXTMENU: { POINT cursorPos; PPH_EMENU menu; PPH_EMENU_ITEM selectedItem; PPLUGIN_NODE selectedNode; GetCursorPos(&cursorPos); if (!(selectedNode = WeGetSelectedWindowNode(&context->TreeContext))) break; menu = PhCreateEMenu(); if (selectedNode->State == PLUGIN_STATE_LOCAL) { HICON shieldIcon; shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_STRICT, 0, 0); selectedItem = PhCreateEMenuItem(0, ID_MENU_UNINSTALL, L"Uninstall", NULL, NULL); if (shieldIcon) { selectedItem->Bitmap = PhIconToBitmap( shieldIcon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON) ); DestroyIcon(shieldIcon); } PhInsertEMenuItem(menu, selectedItem, -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_DISABLE, L"Disable", NULL, NULL), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_PROPERTIES, L"Options", NULL, NULL), -1); if (!selectedNode->PluginInstance || !selectedNode->PluginInstance->Information.HasOptions) { PhSetFlagsEMenuItem(menu, ID_MENU_PROPERTIES, PH_EMENU_DISABLED, PH_EMENU_DISABLED); } if (!PhGetOwnTokenAttributes().Elevated) { PhSetFlagsEMenuItem(menu, ID_MENU_UNINSTALL, PH_EMENU_DISABLED, PH_EMENU_DISABLED); } } else { HICON shieldIcon; shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_STRICT, 0, 0); selectedItem = PhCreateEMenuItem(0, ID_MENU_INSTALL, PhaFormatString(L"Install %s plugin", PhGetStringOrEmpty(selectedNode->Name))->Buffer, NULL, NULL); if (shieldIcon) { selectedItem->Bitmap = PhIconToBitmap( shieldIcon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON) ); DestroyIcon(shieldIcon); } PhInsertEMenuItem(menu, selectedItem, -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_DISABLE, L"Disable", NULL, NULL), -1); if (!PhGetOwnTokenAttributes().Elevated) { PhSetFlagsEMenuItem(menu, ID_MENU_INSTALL, PH_EMENU_DISABLED, PH_EMENU_DISABLED); } } selectedItem = PhShowEMenu( menu, hwndDlg, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, cursorPos.x, cursorPos.y ); if (selectedItem && selectedItem->Id != -1) { switch (selectedItem->Id) { case ID_MENU_INSTALL: { if (!PhGetOwnTokenAttributes().Elevated) { //SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) }; //info.lpFile = L"ProcessHacker.exe"; //info.lpParameters = L"-plugin dmex.ExtraPlugins:INSTALL -plugin dmex.ExtraPlugins:hex64value"; //info.lpVerb = L"runas"; //info.nShow = SW_SHOW; //info.hwnd = hwndDlg; //if (!ShellExecuteEx(&info)) //{ //} } else { PPLUGIN_NODE selectedNode; PPLUGIN_NODE existingNode; if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) { if (selectedNode->State == PLUGIN_STATE_LOCAL) { if (existingNode = FindTreeNode( &context->TreeContext, PLUGIN_STATE_REMOTE, selectedNode->InternalName )) { if (StartInitialCheck(hwndDlg, existingNode, PLUGIN_ACTION_INSTALL)) { } } UpdateTreeView(context); } else { if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL)) { } if (existingNode = FindTreeNode( &context->TreeContext, PLUGIN_STATE_REMOTE, selectedNode->InternalName )) { selectedNode->State = PLUGIN_STATE_RESTART; } UpdateTreeView(context); } } } } break; case ID_MENU_UNINSTALL: { if (!PhGetOwnTokenAttributes().Elevated) { //SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) }; //info.lpFile = L"ProcessHacker.exe"; //info.lpParameters = L"-plugin dmex.ExtraPlugins:UNINSTALL -plugin dmex.ExtraPlugins:hex64value"; //info.lpVerb = L"runas"; //info.nShow = SW_SHOW; //info.hwnd = hwndDlg; // //if (!ShellExecuteEx(&info)) //{ // //} } else { PPLUGIN_NODE selectedNode; PPLUGIN_NODE existingNode; if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) { if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_UNINSTALL)) { if (existingNode = FindTreeNode( &context->TreeContext, PLUGIN_STATE_LOCAL, selectedNode->InternalName )) { existingNode->State = PLUGIN_STATE_RESTART; } } UpdateTreeView(context); } } } break; case ID_MENU_DISABLE: { PPH_STRING baseName; baseName = PhGetBaseName(selectedNode->FileName); PhSetPluginDisabled(&baseName->sr, TRUE); selectedNode->State = PLUGIN_STATE_RESTART; UpdateTreeView(context); SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount())) ); } break; case ID_MENU_PROPERTIES: { if (selectedNode->PluginInstance) { PhInvokeCallback(PhGetPluginCallback((PPH_PLUGIN)selectedNode->PluginInstance, PluginCallbackShowOptions), hwndDlg); } } break; } } } break; case IDCANCEL: case IDOK: { //ShowUpdateDialog(hwndDlg, PLUGIN_ACTION_RESTART); DestroyWindow(hwndDlg); } break; case IDC_DISABLED: { ShowDisabledPluginsDialog(hwndDlg); SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount())) ); } break; } } break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; switch (header->code) { case NM_CUSTOMDRAW: { if (header->idFrom == IDC_INSTALLED || header->idFrom == IDC_BROWSE || header->idFrom == IDC_UPDATES) { SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, DrawButton(context, lParam)); return TRUE; } } break; } } break; case ID_UPDATE_ADD: { PPLUGIN_NODE entry = (PPLUGIN_NODE)lParam; CloudAddChildWindowNode(&context->TreeContext, entry); UpdateTreeView(context); } break; case ID_UPDATE_COUNT: { ULONG count = 0; for (ULONG i = 0; i < context->TreeContext.NodeList->Count; i++) { PPLUGIN_NODE windowNode = context->TreeContext.NodeList->Items[i]; if (windowNode->State == PLUGIN_STATE_UPDATE) { count++; } } SetWindowText(GetDlgItem(hwndDlg, IDC_UPDATES), PhGetString(PhaFormatString(L"Updates (%lu)", count))); } break; } return FALSE; } NTSTATUS CloudDialogThread( _In_ PVOID Parameter ) { BOOL result; MSG message; HWND cloudDialogHandle; PH_AUTO_POOL autoPool; PhInitializeAutoPool(&autoPool); cloudDialogHandle = CreateDialog( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PLUGINS_DIALOG), NULL, CloudPluginsDlgProc ); if (IsIconic(cloudDialogHandle)) ShowWindow(cloudDialogHandle, SW_RESTORE); else ShowWindow(cloudDialogHandle, SW_SHOW); SetForegroundWindow(cloudDialogHandle); while (result = GetMessage(&message, NULL, 0, 0)) { if (result == -1) break; if (!IsDialogMessage(cloudDialogHandle, &message)) { TranslateMessage(&message); DispatchMessage(&message); } PhDrainAutoPool(&autoPool); } PhDeleteAutoPool(&autoPool); return STATUS_SUCCESS; } VOID ShowPluginManagerDialog( VOID ) { HANDLE threadHandle; if (threadHandle = PhCreateThread(0, CloudDialogThread, NULL)) { NtClose(threadHandle); } }