Process Hacker
main.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Extended Services -
3  * main program
4  *
5  * Copyright (C) 2010-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phdk.h>
24 #include "extsrv.h"
25 #include "resource.h"
26 
27 VOID NTAPI LoadCallback(
28  _In_opt_ PVOID Parameter,
29  _In_opt_ PVOID Context
30  );
31 
33  _In_opt_ PVOID Parameter,
34  _In_opt_ PVOID Context
35  );
36 
38  _In_opt_ PVOID Parameter,
39  _In_opt_ PVOID Context
40  );
41 
43  _In_opt_ PVOID Parameter,
44  _In_opt_ PVOID Context
45  );
46 
48  _In_opt_ PVOID Parameter,
49  _In_opt_ PVOID Context
50  );
51 
53  _In_opt_ PVOID Parameter,
54  _In_opt_ PVOID Context
55  );
56 
65 
67  _In_ HINSTANCE Instance,
68  _In_ ULONG Reason,
69  _Reserved_ PVOID Reserved
70  )
71 {
72  switch (Reason)
73  {
74  case DLL_PROCESS_ATTACH:
75  {
77 
78  PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info);
79 
80  if (!PluginInstance)
81  return FALSE;
82 
83  info->DisplayName = L"Extended Services";
84  info->Author = L"wj32";
85  info->Description = L"Extends service management capabilities.";
86  info->Url = L"http://processhacker.sf.net/forums/viewtopic.php?t=1113";
87  info->HasOptions = TRUE;
88 
89  RtlCreateServiceSid_I = PhGetModuleProcAddress(L"ntdll.dll", "RtlCreateServiceSid");
90 
92  PhGetPluginCallback(PluginInstance, PluginCallbackLoad),
94  NULL,
95  &PluginLoadCallbackRegistration
96  );
100  NULL,
101  &PluginShowOptionsCallbackRegistration
102  );
106  NULL,
107  &PluginMenuItemCallbackRegistration
108  );
109 
113  NULL,
114  &ProcessMenuInitializingCallbackRegistration
115  );
119  NULL,
120  &ServicePropertiesInitializingCallbackRegistration
121  );
125  NULL,
126  &ServiceMenuInitializingCallbackRegistration
127  );
128 
129  {
130  static PH_SETTING_CREATE settings[] =
131  {
133  };
134 
135  PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE));
136  }
137  }
138  break;
139  }
140 
141  return TRUE;
142 }
143 
144 VOID NTAPI LoadCallback(
145  _In_opt_ PVOID Parameter,
146  _In_opt_ PVOID Context
147  )
148 {
149  // Nothing
150 }
151 
153  _In_opt_ PVOID Parameter,
154  _In_opt_ PVOID Context
155  )
156 {
157  EsShowOptionsDialog((HWND)Parameter);
158 }
159 
160 VOID NTAPI MenuItemCallback(
161  _In_opt_ PVOID Parameter,
162  _In_opt_ PVOID Context
163  )
164 {
165  PPH_PLUGIN_MENU_ITEM menuItem = Parameter;
166 
167  switch (menuItem->Id)
168  {
170  {
173  }
174  break;
175  case ID_SERVICE_START:
176  {
178  }
179  break;
180  case ID_SERVICE_CONTINUE:
181  {
183  }
184  break;
185  case ID_SERVICE_PAUSE:
186  {
188  }
189  break;
190  case ID_SERVICE_STOP:
191  {
193  }
194  break;
195  case ID_SERVICE_RESTART:
196  {
197  PPH_SERVICE_ITEM serviceItem = menuItem->Context;
198  SC_HANDLE serviceHandle;
199  ULONG win32Result = 0;
200 
201  if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_STATUS))
202  {
203  EsRestartServiceWithProgress(PhMainWndHandle, serviceItem, serviceHandle);
204  CloseServiceHandle(serviceHandle);
205  }
206  else
207  {
208  win32Result = GetLastError();
209  }
210 
211  if (win32Result != 0)
212  {
213  PhShowStatus(
215  PhaFormatString(L"Unable to restart %s", serviceItem->Name->Buffer)->Buffer,
216  0,
217  win32Result
218  );
219  }
220  }
221  break;
222  }
223 }
224 
225 static int __cdecl ServiceForServicesMenuCompare(
226  _In_ const void *elem1,
227  _In_ const void *elem2
228  )
229 {
230  PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)elem1;
231  PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)elem2;
232 
233  return PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE);
234 }
235 
237  _In_opt_ PVOID Parameter,
238  _In_opt_ PVOID Context
239  )
240 {
241  PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter;
242 
243  if (
245  menuInfo->u.Process.NumberOfProcesses == 1 &&
246  menuInfo->u.Process.Processes[0]->ServiceList &&
247  menuInfo->u.Process.Processes[0]->ServiceList->Count != 0
248  )
249  {
250  PPH_PROCESS_ITEM processItem;
251  PPH_EMENU_ITEM servicesMenuItem = NULL;
252  ULONG enumerationKey;
253  PPH_SERVICE_ITEM serviceItem;
254  PPH_LIST serviceList;
255  ULONG i;
256  PPH_EMENU_ITEM priorityMenuItem;
257  ULONG insertIndex;
258 
259  processItem = menuInfo->u.Process.Processes[0];
260 
261  // Create a service list so we can sort it.
262 
263  serviceList = PhCreateList(processItem->ServiceList->Count);
264  enumerationKey = 0;
265 
267 
268  while (PhEnumPointerList(processItem->ServiceList, &enumerationKey, &serviceItem))
269  {
270  PhReferenceObject(serviceItem);
271  // We need to use the service item when the user chooses a menu item.
272  PhAutoDereferenceObject(serviceItem);
273  PhAddItemList(serviceList, serviceItem);
274  }
275 
277 
278  // Sort the service list.
279  qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForServicesMenuCompare);
280 
281  // If there is only one service:
282  // * We use the text "Service (Xxx)".
283  // * There are no extra submenus.
284  if (serviceList->Count != 1)
285  {
286  servicesMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Services", NULL);
287  }
288 
289  // Create and add a menu item for each service.
290 
291  for (i = 0; i < serviceList->Count; i++)
292  {
293  PPH_STRING escapedName;
294  PPH_EMENU_ITEM serviceMenuItem;
295  PPH_EMENU_ITEM startMenuItem;
296  PPH_EMENU_ITEM continueMenuItem;
297  PPH_EMENU_ITEM pauseMenuItem;
298  PPH_EMENU_ITEM stopMenuItem;
299 
300  serviceItem = serviceList->Items[i];
301  escapedName = PhEscapeStringForMenuPrefix(&serviceItem->Name->sr);
302  PhAutoDereferenceObject(escapedName);
303 
304  if (serviceList->Count == 1)
305  {
306  // "Service (Xxx)"
307  escapedName = PhaFormatString(L"Service (%s)", escapedName->Buffer);
308  }
309 
310  serviceMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, escapedName->Buffer, NULL);
311 
312  if (serviceList->Count == 1)
313  {
314  // Make this the root submenu that we will insert.
315  servicesMenuItem = serviceMenuItem;
316  }
317 
318  PhInsertEMenuItem(serviceMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_GOTOSERVICE, L"Go to Service", serviceItem), -1);
319  PhInsertEMenuItem(serviceMenuItem, startMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_START, L"Start", serviceItem), -1);
320  PhInsertEMenuItem(serviceMenuItem, continueMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_CONTINUE, L"Continue", serviceItem), -1);
321  PhInsertEMenuItem(serviceMenuItem, pauseMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_PAUSE, L"Pause", serviceItem), -1);
322  PhInsertEMenuItem(serviceMenuItem, stopMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_STOP, L"Stop", serviceItem), -1);
323 
324  // Massive copy and paste from mainwnd.c.
325  // == START ==
326 
327 #define SET_MENU_ITEM_ENABLED(MenuItem, Enabled) if (!(Enabled)) (MenuItem)->Flags |= PH_EMENU_DISABLED;
328 
329  switch (serviceItem->State)
330  {
331  case SERVICE_RUNNING:
332  {
333  SET_MENU_ITEM_ENABLED(startMenuItem, FALSE);
334  SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE);
335  SET_MENU_ITEM_ENABLED(pauseMenuItem,
336  serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE);
337  SET_MENU_ITEM_ENABLED(stopMenuItem,
338  serviceItem->ControlsAccepted & SERVICE_ACCEPT_STOP);
339  }
340  break;
341  case SERVICE_PAUSED:
342  {
343  SET_MENU_ITEM_ENABLED(startMenuItem, FALSE);
344  SET_MENU_ITEM_ENABLED(continueMenuItem,
345  serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE);
346  SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE);
347  SET_MENU_ITEM_ENABLED(stopMenuItem,
348  serviceItem->ControlsAccepted & SERVICE_ACCEPT_STOP);
349  }
350  break;
351  case SERVICE_STOPPED:
352  {
353  SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE);
354  SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE);
355  SET_MENU_ITEM_ENABLED(stopMenuItem, FALSE);
356  }
357  break;
358  case SERVICE_START_PENDING:
359  case SERVICE_CONTINUE_PENDING:
360  case SERVICE_PAUSE_PENDING:
361  case SERVICE_STOP_PENDING:
362  {
363  SET_MENU_ITEM_ENABLED(startMenuItem, FALSE);
364  SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE);
365  SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE);
366  SET_MENU_ITEM_ENABLED(stopMenuItem, FALSE);
367  }
368  break;
369  }
370 
371  if (!(serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE))
372  {
373  PhDestroyEMenuItem(continueMenuItem);
374  PhDestroyEMenuItem(pauseMenuItem);
375  }
376 
377  // == END ==
378 
379  if (serviceList->Count != 1)
380  PhInsertEMenuItem(servicesMenuItem, serviceMenuItem, -1);
381  }
382 
383  // Destroy the service list (the service items were placed in the auto pool).
384  PhDereferenceObject(serviceList);
385 
386  // Insert our Services menu after the I/O Priority menu.
387 
388  priorityMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"I/O Priority", 0);
389 
390  if (!priorityMenuItem)
391  priorityMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Priority", 0);
392 
393  if (priorityMenuItem)
394  insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, priorityMenuItem) + 1;
395  else
396  insertIndex = 0;
397 
398  PhInsertEMenuItem(menuInfo->Menu, servicesMenuItem, insertIndex);
399  }
400 }
401 
403  _In_opt_ PVOID Parameter,
404  _In_opt_ PVOID Context
405  )
406 {
407  PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter;
408  PROPSHEETPAGE propSheetPage;
409  PPH_SERVICE_ITEM serviceItem;
410 
411  serviceItem = objectProperties->Parameter;
412 
413  // Recovery
414  if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
415  {
416  memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
417  propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
418  propSheetPage.hInstance = PluginInstance->DllBase;
419  propSheetPage.lParam = (LPARAM)serviceItem;
420 
421  if (!(serviceItem->Flags & SERVICE_RUNS_IN_SYSTEM_PROCESS))
422  {
423  propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVRECOVERY);
424  propSheetPage.pfnDlgProc = EspServiceRecoveryDlgProc;
425  }
426  else
427  {
428  // Services which run in system processes don't support failure actions.
429  // Create a different page with a message saying this.
430  propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVRECOVERY2);
431  propSheetPage.pfnDlgProc = EspServiceRecovery2DlgProc;
432  }
433 
434  objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
435  }
436 
437  // Dependencies
438  if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
439  {
440  memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
441  propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
442  propSheetPage.dwFlags = PSP_USETITLE;
443  propSheetPage.hInstance = PluginInstance->DllBase;
444  propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVLIST);
445  propSheetPage.pszTitle = L"Dependencies";
446  propSheetPage.pfnDlgProc = EspServiceDependenciesDlgProc;
447  propSheetPage.lParam = (LPARAM)serviceItem;
448  objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
449  }
450 
451  // Dependents
452  if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
453  {
454  memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
455  propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
456  propSheetPage.dwFlags = PSP_USETITLE;
457  propSheetPage.hInstance = PluginInstance->DllBase;
458  propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVLIST);
459  propSheetPage.pszTitle = L"Dependents";
460  propSheetPage.pfnDlgProc = EspServiceDependentsDlgProc;
461  propSheetPage.lParam = (LPARAM)serviceItem;
462  objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
463  }
464 
465  // Other
466  if (WindowsVersion >= WINDOWS_7 && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
467  {
468  memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
469  propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
470  propSheetPage.dwFlags = PSP_USETITLE;
471  propSheetPage.hInstance = PluginInstance->DllBase;
472  propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVTRIGGERS);
473  propSheetPage.pszTitle = L"Triggers";
474  propSheetPage.pfnDlgProc = EspServiceTriggersDlgProc;
475  propSheetPage.lParam = (LPARAM)serviceItem;
476  objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
477  }
478 
479  // Other
480  if (WindowsVersion >= WINDOWS_VISTA && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
481  {
482  memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
483  propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
484  propSheetPage.dwFlags = PSP_USETITLE;
485  propSheetPage.hInstance = PluginInstance->DllBase;
486  propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVOTHER);
487  propSheetPage.pszTitle = L"Other";
488  propSheetPage.pfnDlgProc = EspServiceOtherDlgProc;
489  propSheetPage.lParam = (LPARAM)serviceItem;
490  objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
491  }
492 }
493 
495  _In_opt_ PVOID Parameter,
496  _In_opt_ PVOID Context
497  )
498 {
499  PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter;
500  PPH_EMENU_ITEM menuItem;
501  ULONG indexOfMenuItem;
502 
503  if (
504  menuInfo->u.Service.NumberOfServices == 1 &&
505  (menuInfo->u.Service.Services[0]->State == SERVICE_RUNNING || menuInfo->u.Service.Services[0]->State == SERVICE_PAUSED)
506  )
507  {
508  // Insert our Restart menu item after the Stop menu item.
509 
510  menuItem = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_STARTSWITH, L"Stop", 0);
511 
512  if (menuItem)
513  indexOfMenuItem = PhIndexOfEMenuItem(menuInfo->Menu, menuItem);
514  else
515  indexOfMenuItem = -1;
516 
518  menuInfo->Menu,
519  PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_RESTART, L"Restart", menuInfo->u.Service.Services[0]),
520  indexOfMenuItem + 1
521  );
522  }
523 }