go my file uploader
This commit is contained in:
481
plugins/ExtendedServices/main.c
Normal file
481
plugins/ExtendedServices/main.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Process Hacker Extended Services -
|
||||
* main program
|
||||
*
|
||||
* Copyright (C) 2010-2015 wj32
|
||||
*
|
||||
* This file is part of Process Hacker.
|
||||
*
|
||||
* Process Hacker is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Process Hacker is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "extsrv.h"
|
||||
|
||||
PPH_PLUGIN PluginInstance;
|
||||
static PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration;
|
||||
static PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration;
|
||||
static PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration;
|
||||
static PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration;
|
||||
static PH_CALLBACK_REGISTRATION ServicePropertiesInitializingCallbackRegistration;
|
||||
static PH_CALLBACK_REGISTRATION ServiceMenuInitializingCallbackRegistration;
|
||||
|
||||
VOID NTAPI LoadCallback(
|
||||
_In_opt_ PVOID Parameter,
|
||||
_In_opt_ PVOID Context
|
||||
)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
VOID NTAPI ShowOptionsCallback(
|
||||
_In_opt_ PVOID Parameter,
|
||||
_In_opt_ PVOID Context
|
||||
)
|
||||
{
|
||||
EsShowOptionsDialog((HWND)Parameter);
|
||||
}
|
||||
|
||||
VOID NTAPI MenuItemCallback(
|
||||
_In_opt_ PVOID Parameter,
|
||||
_In_opt_ PVOID Context
|
||||
)
|
||||
{
|
||||
PPH_PLUGIN_MENU_ITEM menuItem = Parameter;
|
||||
|
||||
switch (menuItem->Id)
|
||||
{
|
||||
case ID_SERVICE_GOTOSERVICE:
|
||||
{
|
||||
ProcessHacker_SelectTabPage(PhMainWndHandle, 1);
|
||||
ProcessHacker_SelectServiceItem(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context);
|
||||
}
|
||||
break;
|
||||
case ID_SERVICE_START:
|
||||
{
|
||||
PhUiStartService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context);
|
||||
}
|
||||
break;
|
||||
case ID_SERVICE_CONTINUE:
|
||||
{
|
||||
PhUiContinueService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context);
|
||||
}
|
||||
break;
|
||||
case ID_SERVICE_PAUSE:
|
||||
{
|
||||
PhUiPauseService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context);
|
||||
}
|
||||
break;
|
||||
case ID_SERVICE_STOP:
|
||||
{
|
||||
PhUiStopService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context);
|
||||
}
|
||||
break;
|
||||
case ID_SERVICE_RESTART:
|
||||
{
|
||||
PPH_SERVICE_ITEM serviceItem = menuItem->Context;
|
||||
SC_HANDLE serviceHandle;
|
||||
ULONG win32Result = 0;
|
||||
|
||||
if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_STATUS))
|
||||
{
|
||||
EsRestartServiceWithProgress(PhMainWndHandle, serviceItem, serviceHandle);
|
||||
CloseServiceHandle(serviceHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
win32Result = GetLastError();
|
||||
}
|
||||
|
||||
if (win32Result != 0)
|
||||
{
|
||||
PhShowStatus(
|
||||
PhMainWndHandle,
|
||||
PhaFormatString(L"Unable to restart %s", serviceItem->Name->Buffer)->Buffer,
|
||||
0,
|
||||
win32Result
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int __cdecl ServiceForServicesMenuCompare(
|
||||
_In_ const void *elem1,
|
||||
_In_ const void *elem2
|
||||
)
|
||||
{
|
||||
PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)elem1;
|
||||
PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)elem2;
|
||||
|
||||
return PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE);
|
||||
}
|
||||
|
||||
VOID NTAPI ProcessMenuInitializingCallback(
|
||||
_In_opt_ PVOID Parameter,
|
||||
_In_opt_ PVOID Context
|
||||
)
|
||||
{
|
||||
PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter;
|
||||
|
||||
if (
|
||||
PhGetIntegerSetting(SETTING_NAME_ENABLE_SERVICES_MENU) &&
|
||||
menuInfo->u.Process.NumberOfProcesses == 1 &&
|
||||
menuInfo->u.Process.Processes[0]->ServiceList &&
|
||||
menuInfo->u.Process.Processes[0]->ServiceList->Count != 0
|
||||
)
|
||||
{
|
||||
PPH_PROCESS_ITEM processItem;
|
||||
PPH_EMENU_ITEM servicesMenuItem = NULL;
|
||||
ULONG enumerationKey;
|
||||
PPH_SERVICE_ITEM serviceItem;
|
||||
PPH_LIST serviceList;
|
||||
ULONG i;
|
||||
PPH_EMENU_ITEM priorityMenuItem;
|
||||
ULONG insertIndex;
|
||||
|
||||
processItem = menuInfo->u.Process.Processes[0];
|
||||
|
||||
// Create a service list so we can sort it.
|
||||
|
||||
serviceList = PH_AUTO(PhCreateList(processItem->ServiceList->Count));
|
||||
enumerationKey = 0;
|
||||
|
||||
PhAcquireQueuedLockShared(&processItem->ServiceListLock);
|
||||
|
||||
while (PhEnumPointerList(processItem->ServiceList, &enumerationKey, &serviceItem))
|
||||
{
|
||||
PhReferenceObject(serviceItem);
|
||||
// We need to use the service item when the user chooses a menu item.
|
||||
PH_AUTO(serviceItem);
|
||||
PhAddItemList(serviceList, serviceItem);
|
||||
}
|
||||
|
||||
PhReleaseQueuedLockShared(&processItem->ServiceListLock);
|
||||
|
||||
// Sort the service list.
|
||||
qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForServicesMenuCompare);
|
||||
|
||||
// If there is only one service:
|
||||
// * We use the text "Service (Xxx)".
|
||||
// * There are no extra submenus.
|
||||
if (serviceList->Count != 1)
|
||||
{
|
||||
servicesMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Services", NULL);
|
||||
}
|
||||
|
||||
// Create and add a menu item for each service.
|
||||
|
||||
for (i = 0; i < serviceList->Count; i++)
|
||||
{
|
||||
PPH_STRING escapedName;
|
||||
PPH_EMENU_ITEM serviceMenuItem;
|
||||
PPH_EMENU_ITEM startMenuItem;
|
||||
PPH_EMENU_ITEM continueMenuItem;
|
||||
PPH_EMENU_ITEM pauseMenuItem;
|
||||
PPH_EMENU_ITEM stopMenuItem;
|
||||
|
||||
serviceItem = serviceList->Items[i];
|
||||
escapedName = PH_AUTO(PhEscapeStringForMenuPrefix(&serviceItem->Name->sr));
|
||||
|
||||
if (serviceList->Count == 1)
|
||||
{
|
||||
// "Service (Xxx)"
|
||||
escapedName = PhaFormatString(L"Service (%s)", escapedName->Buffer);
|
||||
}
|
||||
|
||||
serviceMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, escapedName->Buffer, NULL);
|
||||
|
||||
if (serviceList->Count == 1)
|
||||
{
|
||||
// Make this the root submenu that we will insert.
|
||||
servicesMenuItem = serviceMenuItem;
|
||||
}
|
||||
|
||||
PhInsertEMenuItem(serviceMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_GOTOSERVICE, L"Go to service", serviceItem), -1);
|
||||
PhInsertEMenuItem(serviceMenuItem, startMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_START, L"Start", serviceItem), -1);
|
||||
PhInsertEMenuItem(serviceMenuItem, continueMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_CONTINUE, L"Continue", serviceItem), -1);
|
||||
PhInsertEMenuItem(serviceMenuItem, pauseMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_PAUSE, L"Pause", serviceItem), -1);
|
||||
PhInsertEMenuItem(serviceMenuItem, stopMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_STOP, L"Stop", serviceItem), -1);
|
||||
|
||||
// Massive copy and paste from mainwnd.c.
|
||||
// == START ==
|
||||
|
||||
#define SET_MENU_ITEM_ENABLED(MenuItem, Enabled) if (!(Enabled)) (MenuItem)->Flags |= PH_EMENU_DISABLED;
|
||||
|
||||
switch (serviceItem->State)
|
||||
{
|
||||
case SERVICE_RUNNING:
|
||||
{
|
||||
SET_MENU_ITEM_ENABLED(startMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(pauseMenuItem,
|
||||
serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE);
|
||||
SET_MENU_ITEM_ENABLED(stopMenuItem,
|
||||
serviceItem->ControlsAccepted & SERVICE_ACCEPT_STOP);
|
||||
}
|
||||
break;
|
||||
case SERVICE_PAUSED:
|
||||
{
|
||||
SET_MENU_ITEM_ENABLED(startMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(continueMenuItem,
|
||||
serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE);
|
||||
SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(stopMenuItem,
|
||||
serviceItem->ControlsAccepted & SERVICE_ACCEPT_STOP);
|
||||
}
|
||||
break;
|
||||
case SERVICE_STOPPED:
|
||||
{
|
||||
SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(stopMenuItem, FALSE);
|
||||
}
|
||||
break;
|
||||
case SERVICE_START_PENDING:
|
||||
case SERVICE_CONTINUE_PENDING:
|
||||
case SERVICE_PAUSE_PENDING:
|
||||
case SERVICE_STOP_PENDING:
|
||||
{
|
||||
SET_MENU_ITEM_ENABLED(startMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE);
|
||||
SET_MENU_ITEM_ENABLED(stopMenuItem, FALSE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE))
|
||||
{
|
||||
PhDestroyEMenuItem(continueMenuItem);
|
||||
PhDestroyEMenuItem(pauseMenuItem);
|
||||
}
|
||||
|
||||
// == END ==
|
||||
|
||||
if (serviceList->Count != 1)
|
||||
PhInsertEMenuItem(servicesMenuItem, serviceMenuItem, -1);
|
||||
}
|
||||
|
||||
// Insert our Services menu after the I/O Priority menu.
|
||||
|
||||
priorityMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"I/O Priority", 0);
|
||||
|
||||
if (!priorityMenuItem)
|
||||
priorityMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Priority", 0);
|
||||
|
||||
if (priorityMenuItem)
|
||||
insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, priorityMenuItem) + 1;
|
||||
else
|
||||
insertIndex = 0;
|
||||
|
||||
PhInsertEMenuItem(menuInfo->Menu, servicesMenuItem, insertIndex);
|
||||
}
|
||||
}
|
||||
|
||||
NTAPI ServicePropertiesInitializingCallback(
|
||||
_In_opt_ PVOID Parameter,
|
||||
_In_opt_ PVOID Context
|
||||
)
|
||||
{
|
||||
PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter;
|
||||
PROPSHEETPAGE propSheetPage;
|
||||
PPH_SERVICE_ITEM serviceItem;
|
||||
|
||||
serviceItem = objectProperties->Parameter;
|
||||
|
||||
// Recovery
|
||||
if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
|
||||
{
|
||||
memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
|
||||
propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
|
||||
propSheetPage.hInstance = PluginInstance->DllBase;
|
||||
propSheetPage.lParam = (LPARAM)serviceItem;
|
||||
|
||||
if (!(serviceItem->Flags & SERVICE_RUNS_IN_SYSTEM_PROCESS))
|
||||
{
|
||||
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVRECOVERY);
|
||||
propSheetPage.pfnDlgProc = EspServiceRecoveryDlgProc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Services which run in system processes don't support failure actions.
|
||||
// Create a different page with a message saying this.
|
||||
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVRECOVERY2);
|
||||
propSheetPage.pfnDlgProc = EspServiceRecovery2DlgProc;
|
||||
}
|
||||
|
||||
objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
|
||||
{
|
||||
memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
|
||||
propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
|
||||
propSheetPage.dwFlags = PSP_USETITLE;
|
||||
propSheetPage.hInstance = PluginInstance->DllBase;
|
||||
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVLIST);
|
||||
propSheetPage.pszTitle = L"Dependencies";
|
||||
propSheetPage.pfnDlgProc = EspServiceDependenciesDlgProc;
|
||||
propSheetPage.lParam = (LPARAM)serviceItem;
|
||||
objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
|
||||
}
|
||||
|
||||
// Dependents
|
||||
if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
|
||||
{
|
||||
memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
|
||||
propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
|
||||
propSheetPage.dwFlags = PSP_USETITLE;
|
||||
propSheetPage.hInstance = PluginInstance->DllBase;
|
||||
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVLIST);
|
||||
propSheetPage.pszTitle = L"Dependents";
|
||||
propSheetPage.pfnDlgProc = EspServiceDependentsDlgProc;
|
||||
propSheetPage.lParam = (LPARAM)serviceItem;
|
||||
objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
|
||||
}
|
||||
|
||||
// Other
|
||||
if (WindowsVersion >= WINDOWS_7 && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
|
||||
{
|
||||
memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
|
||||
propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
|
||||
propSheetPage.dwFlags = PSP_USETITLE;
|
||||
propSheetPage.hInstance = PluginInstance->DllBase;
|
||||
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVTRIGGERS);
|
||||
propSheetPage.pszTitle = L"Triggers";
|
||||
propSheetPage.pfnDlgProc = EspServiceTriggersDlgProc;
|
||||
propSheetPage.lParam = (LPARAM)serviceItem;
|
||||
objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
|
||||
}
|
||||
|
||||
// Other
|
||||
if (WindowsVersion >= WINDOWS_VISTA && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
|
||||
{
|
||||
memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
|
||||
propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
|
||||
propSheetPage.dwFlags = PSP_USETITLE;
|
||||
propSheetPage.hInstance = PluginInstance->DllBase;
|
||||
propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVOTHER);
|
||||
propSheetPage.pszTitle = L"Other";
|
||||
propSheetPage.pfnDlgProc = EspServiceOtherDlgProc;
|
||||
propSheetPage.lParam = (LPARAM)serviceItem;
|
||||
objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
|
||||
}
|
||||
}
|
||||
|
||||
VOID NTAPI ServiceMenuInitializingCallback(
|
||||
_In_opt_ PVOID Parameter,
|
||||
_In_opt_ PVOID Context
|
||||
)
|
||||
{
|
||||
PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter;
|
||||
PPH_EMENU_ITEM menuItem;
|
||||
ULONG indexOfMenuItem;
|
||||
|
||||
if (
|
||||
menuInfo->u.Service.NumberOfServices == 1 &&
|
||||
(menuInfo->u.Service.Services[0]->State == SERVICE_RUNNING || menuInfo->u.Service.Services[0]->State == SERVICE_PAUSED)
|
||||
)
|
||||
{
|
||||
// Insert our Restart menu item after the Stop menu item.
|
||||
|
||||
menuItem = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_STARTSWITH, L"Stop", 0);
|
||||
|
||||
if (menuItem)
|
||||
indexOfMenuItem = PhIndexOfEMenuItem(menuInfo->Menu, menuItem);
|
||||
else
|
||||
indexOfMenuItem = -1;
|
||||
|
||||
PhInsertEMenuItem(
|
||||
menuInfo->Menu,
|
||||
PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_RESTART, L"Restart", menuInfo->u.Service.Services[0]),
|
||||
indexOfMenuItem + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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_ENABLE_SERVICES_MENU, L"1" }
|
||||
};
|
||||
|
||||
PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info);
|
||||
|
||||
if (!PluginInstance)
|
||||
return FALSE;
|
||||
|
||||
info->DisplayName = L"Extended Services";
|
||||
info->Author = L"wj32";
|
||||
info->Description = L"Extends service management capabilities.";
|
||||
info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1113";
|
||||
info->HasOptions = TRUE;
|
||||
|
||||
PhRegisterCallback(
|
||||
PhGetPluginCallback(PluginInstance, PluginCallbackLoad),
|
||||
LoadCallback,
|
||||
NULL,
|
||||
&PluginLoadCallbackRegistration
|
||||
);
|
||||
PhRegisterCallback(
|
||||
PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions),
|
||||
ShowOptionsCallback,
|
||||
NULL,
|
||||
&PluginShowOptionsCallbackRegistration
|
||||
);
|
||||
PhRegisterCallback(
|
||||
PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem),
|
||||
MenuItemCallback,
|
||||
NULL,
|
||||
&PluginMenuItemCallbackRegistration
|
||||
);
|
||||
|
||||
PhRegisterCallback(
|
||||
PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing),
|
||||
ProcessMenuInitializingCallback,
|
||||
NULL,
|
||||
&ProcessMenuInitializingCallbackRegistration
|
||||
);
|
||||
PhRegisterCallback(
|
||||
PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing),
|
||||
ServicePropertiesInitializingCallback,
|
||||
NULL,
|
||||
&ServicePropertiesInitializingCallbackRegistration
|
||||
);
|
||||
PhRegisterCallback(
|
||||
PhGetGeneralCallback(GeneralCallbackServiceMenuInitializing),
|
||||
ServiceMenuInitializingCallback,
|
||||
NULL,
|
||||
&ServiceMenuInitializingCallbackRegistration
|
||||
);
|
||||
|
||||
PhAddSettings(settings, ARRAYSIZE(settings));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
Reference in New Issue
Block a user