Process Hacker
recovery.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Extended Services -
3  * recovery information
4  *
5  * Copyright (C) 2010-2011 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 <windowsx.h>
25 #include "extsrv.h"
26 #include "resource.h"
27 
28 typedef struct _SERVICE_RECOVERY_CONTEXT
29 {
30  PPH_SERVICE_ITEM ServiceItem;
31 
32  ULONG NumberOfActions;
33  BOOLEAN EnableFlagCheckBox;
34  ULONG RebootAfter; // in ms
35  PPH_STRING RebootMessage;
36 
37  BOOLEAN Ready;
38  BOOLEAN Dirty;
40 
41 #define SIP(String, Integer) { (String), (PVOID)(Integer) }
42 
43 static PH_KEY_VALUE_PAIR ServiceActionPairs[] =
44 {
45  SIP(L"Take no action", SC_ACTION_NONE),
46  SIP(L"Restart the service", SC_ACTION_RESTART),
47  SIP(L"Run a program", SC_ACTION_RUN_COMMAND),
48  SIP(L"Restart the computer", SC_ACTION_REBOOT)
49 };
50 
51 INT_PTR CALLBACK RestartComputerDlgProc(
52  _In_ HWND hwndDlg,
53  _In_ UINT uMsg,
54  _In_ WPARAM wParam,
55  _In_ LPARAM lParam
56  );
57 
59  _In_ HWND ComboBoxHandle
60  )
61 {
62  ULONG i;
63 
64  for (i = 0; i < sizeof(ServiceActionPairs) / sizeof(PH_KEY_VALUE_PAIR); i++)
65  ComboBox_AddString(ComboBoxHandle, (PWSTR)ServiceActionPairs[i].Key);
66 
67  PhSelectComboBoxString(ComboBoxHandle, (PWSTR)ServiceActionPairs[0].Key, FALSE);
68 }
69 
70 SC_ACTION_TYPE EspStringToServiceAction(
71  _In_ PWSTR String
72  )
73 {
74  ULONG integer;
75 
76  if (PhFindIntegerSiKeyValuePairs(ServiceActionPairs, sizeof(ServiceActionPairs), String, &integer))
77  return integer;
78  else
79  return 0;
80 }
81 
83  _In_ SC_ACTION_TYPE ActionType
84  )
85 {
86  PWSTR string;
87 
88  if (PhFindStringSiKeyValuePairs(ServiceActionPairs, sizeof(ServiceActionPairs), ActionType, &string))
89  return string;
90  else
91  return NULL;
92 }
93 
94 static SC_ACTION_TYPE ComboBoxToServiceAction(
95  _In_ HWND ComboBoxHandle
96  )
97 {
98  SC_ACTION_TYPE actionType;
99  PPH_STRING string;
100 
101  string = PhGetComboBoxString(ComboBoxHandle, ComboBox_GetCurSel(ComboBoxHandle));
102 
103  if (!string)
104  return SC_ACTION_NONE;
105 
106  actionType = EspStringToServiceAction(string->Buffer);
107  PhDereferenceObject(string);
108 
109  return actionType;
110 }
111 
112 static VOID ServiceActionToComboBox(
113  _In_ HWND ComboBoxHandle,
114  _In_ SC_ACTION_TYPE ActionType
115  )
116 {
117  PWSTR string;
118 
119  if (string = EspServiceActionToString(ActionType))
120  PhSelectComboBoxString(ComboBoxHandle, string, FALSE);
121  else
122  PhSelectComboBoxString(ComboBoxHandle, (PWSTR)ServiceActionPairs[0].Key, FALSE);
123 }
124 
125 static VOID EspFixControls(
126  _In_ HWND hwndDlg,
127  _In_ PSERVICE_RECOVERY_CONTEXT Context
128  )
129 {
130  SC_ACTION_TYPE action1;
131  SC_ACTION_TYPE action2;
132  SC_ACTION_TYPE actionS;
133  BOOLEAN enableRestart;
134  BOOLEAN enableReboot;
135  BOOLEAN enableCommand;
136 
137  action1 = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE));
138  action2 = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SECONDFAILURE));
139  actionS = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES));
140 
141  EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), Context->EnableFlagCheckBox);
142 
143  enableRestart = action1 == SC_ACTION_RESTART || action2 == SC_ACTION_RESTART || actionS == SC_ACTION_RESTART;
144  enableReboot = action1 == SC_ACTION_REBOOT || action2 == SC_ACTION_REBOOT || actionS == SC_ACTION_REBOOT;
145  enableCommand = action1 == SC_ACTION_RUN_COMMAND || action2 == SC_ACTION_RUN_COMMAND || actionS == SC_ACTION_RUN_COMMAND;
146 
147  EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER_LABEL), enableRestart);
148  EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER), enableRestart);
149  EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER_MINUTES), enableRestart);
150 
151  EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTCOMPUTEROPTIONS), enableReboot);
152 
153  EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_GROUP), enableCommand);
154  EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_LABEL), enableCommand);
155  EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM), enableCommand);
156  EnableWindow(GetDlgItem(hwndDlg, IDC_BROWSE), enableCommand);
157  EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_INFO), enableCommand);
158 }
159 
161  _In_ HWND hwndDlg,
162  _In_ PSERVICE_RECOVERY_CONTEXT Context
163  )
164 {
165  NTSTATUS status = STATUS_SUCCESS;
166  SC_HANDLE serviceHandle;
167  LPSERVICE_FAILURE_ACTIONS failureActions;
168  SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag;
169  SC_ACTION_TYPE lastType;
170  ULONG returnLength;
171  ULONG i;
172 
173  if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)))
174  return NTSTATUS_FROM_WIN32(GetLastError());
175 
176  if (!(failureActions = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS)))
177  {
178  CloseServiceHandle(serviceHandle);
179  return NTSTATUS_FROM_WIN32(GetLastError());
180  }
181 
182  // Failure action types
183 
184  Context->NumberOfActions = failureActions->cActions;
185 
186  if (failureActions->cActions != 0 && failureActions->cActions != 3)
187  status = STATUS_SOME_NOT_MAPPED;
188 
189  // If failure actions are not defined for a particular fail count, the
190  // last failure action is used. Here we duplicate this behaviour when there
191  // are fewer than 3 failure actions.
192  lastType = SC_ACTION_NONE;
193 
194  ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE),
195  failureActions->cActions >= 1 ? (lastType = failureActions->lpsaActions[0].Type) : lastType);
196  ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_SECONDFAILURE),
197  failureActions->cActions >= 2 ? (lastType = failureActions->lpsaActions[1].Type) : lastType);
198  ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES),
199  failureActions->cActions >= 3 ? (lastType = failureActions->lpsaActions[2].Type) : lastType);
200 
201  // Reset fail count after
202 
203  SetDlgItemInt(hwndDlg, IDC_RESETFAILCOUNT, failureActions->dwResetPeriod / (60 * 60 * 24), FALSE); // s to days
204 
205  // Restart service after
206 
207  SetDlgItemText(hwndDlg, IDC_RESTARTSERVICEAFTER, L"1");
208 
209  for (i = 0; i < failureActions->cActions; i++)
210  {
211  if (failureActions->lpsaActions[i].Type == SC_ACTION_RESTART)
212  {
213  if (failureActions->lpsaActions[i].Delay != 0)
214  {
215  SetDlgItemInt(hwndDlg, IDC_RESTARTSERVICEAFTER,
216  failureActions->lpsaActions[i].Delay / (1000 * 60), FALSE); // ms to min
217  }
218 
219  break;
220  }
221  }
222 
223  // Enable actions for stops with errors
224 
225  // This is Vista and above only.
226  if (WindowsVersion >= WINDOWS_VISTA && QueryServiceConfig2(
227  serviceHandle,
228  SERVICE_CONFIG_FAILURE_ACTIONS_FLAG,
229  (BYTE *)&failureActionsFlag,
230  sizeof(SERVICE_FAILURE_ACTIONS_FLAG),
231  &returnLength
232  ))
233  {
234  Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS),
235  failureActionsFlag.fFailureActionsOnNonCrashFailures ? BST_CHECKED : BST_UNCHECKED);
236  Context->EnableFlagCheckBox = TRUE;
237  }
238  else
239  {
240  Context->EnableFlagCheckBox = FALSE;
241  }
242 
243  // Restart computer options
244 
245  Context->RebootAfter = 1 * 1000 * 60;
246 
247  for (i = 0; i < failureActions->cActions; i++)
248  {
249  if (failureActions->lpsaActions[i].Type == SC_ACTION_REBOOT)
250  {
251  if (failureActions->lpsaActions[i].Delay != 0)
252  Context->RebootAfter = failureActions->lpsaActions[i].Delay;
253 
254  break;
255  }
256  }
257 
258  if (failureActions->lpRebootMsg && failureActions->lpRebootMsg[0] != 0)
259  PhMoveReference(&Context->RebootMessage, PhCreateString(failureActions->lpRebootMsg));
260  else
261  PhMoveReference(&Context->RebootMessage, NULL);
262 
263  // Run program
264 
265  SetDlgItemText(hwndDlg, IDC_RUNPROGRAM, failureActions->lpCommand);
266 
267  PhFree(failureActions);
268  CloseServiceHandle(serviceHandle);
269 
270  return status;
271 }
272 
273 INT_PTR CALLBACK EspServiceRecoveryDlgProc(
274  _In_ HWND hwndDlg,
275  _In_ UINT uMsg,
276  _In_ WPARAM wParam,
277  _In_ LPARAM lParam
278  )
279 {
281 
282  if (uMsg == WM_INITDIALOG)
283  {
284  context = PhAllocate(sizeof(SERVICE_RECOVERY_CONTEXT));
285  memset(context, 0, sizeof(SERVICE_RECOVERY_CONTEXT));
286 
287  SetProp(hwndDlg, L"Context", (HANDLE)context);
288  }
289  else
290  {
291  context = (PSERVICE_RECOVERY_CONTEXT)GetProp(hwndDlg, L"Context");
292 
293  if (uMsg == WM_DESTROY)
294  RemoveProp(hwndDlg, L"Context");
295  }
296 
297  if (!context)
298  return FALSE;
299 
300  switch (uMsg)
301  {
302  case WM_INITDIALOG:
303  {
304  NTSTATUS status;
305  LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam;
306  PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam;
307 
308  context->ServiceItem = serviceItem;
309 
310  EspAddServiceActionStrings(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE));
311  EspAddServiceActionStrings(GetDlgItem(hwndDlg, IDC_SECONDFAILURE));
313 
314  status = EspLoadRecoveryInfo(hwndDlg, context);
315 
316  if (status == STATUS_SOME_NOT_MAPPED)
317  {
318  if (context->NumberOfActions > 3)
319  {
321  hwndDlg,
322  L"The service has %lu failure actions configured, but this program only supports editing 3. "
323  L"If you save the recovery information using this program, the additional failure actions will be lost.",
324  context->NumberOfActions
325  );
326  }
327  }
328  else if (!NT_SUCCESS(status))
329  {
330  SetDlgItemText(hwndDlg, IDC_RESETFAILCOUNT, L"0");
331 
333  {
334  context->EnableFlagCheckBox = TRUE;
335  EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), TRUE);
336  }
337 
338  PhShowWarning(hwndDlg, L"Unable to query service recovery information: %s",
340  }
341 
342  EspFixControls(hwndDlg, context);
343 
344  context->Ready = TRUE;
345  }
346  break;
347  case WM_DESTROY:
348  {
349  PhMoveReference(&context->RebootMessage, NULL);
350  PhFree(context);
351  }
352  break;
353  case WM_COMMAND:
354  {
355  switch (LOWORD(wParam))
356  {
357  case IDC_FIRSTFAILURE:
358  case IDC_SECONDFAILURE:
360  {
361  if (HIWORD(wParam) == CBN_SELCHANGE)
362  {
363  EspFixControls(hwndDlg, context);
364  }
365  }
366  break;
368  {
369  DialogBoxParam(
371  MAKEINTRESOURCE(IDD_RESTARTCOMP),
372  hwndDlg,
374  (LPARAM)context
375  );
376  }
377  break;
378  case IDC_BROWSE:
379  {
380  static PH_FILETYPE_FILTER filters[] =
381  {
382  { L"Executable files (*.exe;*.cmd;*.bat)", L"*.exe;*.cmd;*.bat" },
383  { L"All files (*.*)", L"*.*" }
384  };
385  PVOID fileDialog;
386  PPH_STRING fileName;
387 
388  fileDialog = PhCreateOpenFileDialog();
389  PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
390 
391  fileName = PhaGetDlgItemText(hwndDlg, IDC_RUNPROGRAM);
392  PhSetFileDialogFileName(fileDialog, fileName->Buffer);
393 
394  if (PhShowFileDialog(hwndDlg, fileDialog))
395  {
396  fileName = PhGetFileDialogFileName(fileDialog);
397  SetDlgItemText(hwndDlg, IDC_RUNPROGRAM, fileName->Buffer);
398  PhDereferenceObject(fileName);
399  }
400 
401  PhFreeFileDialog(fileDialog);
402  }
403  break;
405  {
406  context->Dirty = TRUE;
407  }
408  break;
409  }
410 
411  switch (HIWORD(wParam))
412  {
413  case EN_CHANGE:
414  case CBN_SELCHANGE:
415  {
416  if (context->Ready)
417  context->Dirty = TRUE;
418  }
419  break;
420  }
421  }
422  break;
423  case WM_NOTIFY:
424  {
425  LPNMHDR header = (LPNMHDR)lParam;
426 
427  switch (header->code)
428  {
429  case PSN_KILLACTIVE:
430  {
431  SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE);
432  }
433  return TRUE;
434  case PSN_APPLY:
435  {
436  NTSTATUS status;
437  PPH_SERVICE_ITEM serviceItem = context->ServiceItem;
438  SC_HANDLE serviceHandle;
439  ULONG restartServiceAfter;
440  SERVICE_FAILURE_ACTIONS failureActions;
441  SC_ACTION actions[3];
442  ULONG i;
443  BOOLEAN enableRestart = FALSE;
444 
445  SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
446 
447  if (!context->Dirty)
448  {
449  return TRUE;
450  }
451 
452  // Build the failure actions structure.
453 
454  failureActions.dwResetPeriod = GetDlgItemInt(hwndDlg, IDC_RESETFAILCOUNT, NULL, FALSE) * 60 * 60 * 24;
455  failureActions.lpRebootMsg = PhGetStringOrEmpty(context->RebootMessage);
456  failureActions.lpCommand = PhaGetDlgItemText(hwndDlg, IDC_RUNPROGRAM)->Buffer;
457  failureActions.cActions = 3;
458  failureActions.lpsaActions = actions;
459 
460  actions[0].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE));
461  actions[1].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SECONDFAILURE));
462  actions[2].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES));
463 
464  restartServiceAfter = GetDlgItemInt(hwndDlg, IDC_RESTARTSERVICEAFTER, NULL, FALSE) * 1000 * 60;
465 
466  for (i = 0; i < 3; i++)
467  {
468  switch (actions[i].Type)
469  {
470  case SC_ACTION_RESTART:
471  actions[i].Delay = restartServiceAfter;
472  enableRestart = TRUE;
473  break;
474  case SC_ACTION_REBOOT:
475  actions[i].Delay = context->RebootAfter;
476  break;
477  case SC_ACTION_RUN_COMMAND:
478  actions[i].Delay = 0;
479  break;
480  }
481  }
482 
483  // Try to save the changes.
484 
485  serviceHandle = PhOpenService(
486  serviceItem->Name->Buffer,
487  SERVICE_CHANGE_CONFIG | (enableRestart ? SERVICE_START : 0) // SC_ACTION_RESTART requires SERVICE_START
488  );
489 
490  if (serviceHandle)
491  {
492  if (ChangeServiceConfig2(
493  serviceHandle,
494  SERVICE_CONFIG_FAILURE_ACTIONS,
495  &failureActions
496  ))
497  {
498  if (context->EnableFlagCheckBox)
499  {
500  SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag;
501 
502  failureActionsFlag.fFailureActionsOnNonCrashFailures =
503  Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS)) == BST_CHECKED;
504 
505  ChangeServiceConfig2(
506  serviceHandle,
507  SERVICE_CONFIG_FAILURE_ACTIONS_FLAG,
508  &failureActionsFlag
509  );
510  }
511 
512  CloseServiceHandle(serviceHandle);
513  }
514  else
515  {
516  CloseServiceHandle(serviceHandle);
517  goto ErrorCase;
518  }
519  }
520  else
521  {
522  if (GetLastError() == ERROR_ACCESS_DENIED && !PhElevated)
523  {
524  // Elevate using phsvc.
525  if (PhUiConnectToPhSvc(hwndDlg, FALSE))
526  {
528  serviceItem->Name->Buffer,
529  SERVICE_CONFIG_FAILURE_ACTIONS,
530  &failureActions
531  )))
532  {
533  if (context->EnableFlagCheckBox)
534  {
535  SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag;
536 
537  failureActionsFlag.fFailureActionsOnNonCrashFailures =
538  Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS)) == BST_CHECKED;
539 
541  serviceItem->Name->Buffer,
542  SERVICE_CONFIG_FAILURE_ACTIONS_FLAG,
543  &failureActionsFlag
544  );
545  }
546  }
547 
549 
550  if (!NT_SUCCESS(status))
551  {
552  SetLastError(PhNtStatusToDosError(status));
553  goto ErrorCase;
554  }
555  }
556  else
557  {
558  // User cancelled elevation.
559  SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID);
560  }
561  }
562  else
563  {
564  goto ErrorCase;
565  }
566  }
567 
568  return TRUE;
569 ErrorCase:
570  if (PhShowMessage(
571  hwndDlg,
572  MB_ICONERROR | MB_RETRYCANCEL,
573  L"Unable to change service recovery information: %s",
574  ((PPH_STRING)PhAutoDereferenceObject(PhGetWin32Message(GetLastError())))->Buffer
575  ) == IDRETRY)
576  {
577  SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID);
578  }
579  }
580  return TRUE;
581  }
582  }
583  break;
584  }
585 
586  return FALSE;
587 }
588 
589 INT_PTR CALLBACK EspServiceRecovery2DlgProc(
590  _In_ HWND hwndDlg,
591  _In_ UINT uMsg,
592  _In_ WPARAM wParam,
593  _In_ LPARAM lParam
594  )
595 {
596  return FALSE;
597 }
598 
599 static INT_PTR CALLBACK RestartComputerDlgProc(
600  _In_ HWND hwndDlg,
601  _In_ UINT uMsg,
602  _In_ WPARAM wParam,
603  _In_ LPARAM lParam
604  )
605 {
607 
608  if (uMsg == WM_INITDIALOG)
609  {
610  context = (PSERVICE_RECOVERY_CONTEXT)lParam;
611  SetProp(hwndDlg, L"Context", (HANDLE)context);
612  }
613  else
614  {
615  context = (PSERVICE_RECOVERY_CONTEXT)GetProp(hwndDlg, L"Context");
616 
617  if (uMsg == WM_DESTROY)
618  RemoveProp(hwndDlg, L"Context");
619  }
620 
621  if (!context)
622  return FALSE;
623 
624  switch (uMsg)
625  {
626  case WM_INITDIALOG:
627  {
628  SetDlgItemInt(hwndDlg, IDC_RESTARTCOMPAFTER, context->RebootAfter / (1000 * 60), FALSE); // ms to min
629  Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE), context->RebootMessage ? BST_CHECKED : BST_UNCHECKED);
630  SetDlgItemText(hwndDlg, IDC_RESTARTMESSAGE, PhGetString(context->RebootMessage));
631 
632  SetFocus(GetDlgItem(hwndDlg, IDC_RESTARTCOMPAFTER));
633  Edit_SetSel(GetDlgItem(hwndDlg, IDC_RESTARTCOMPAFTER), 0, -1);
634  }
635  break;
636  case WM_COMMAND:
637  {
638  switch (LOWORD(wParam))
639  {
640  case IDCANCEL:
641  EndDialog(hwndDlg, IDCANCEL);
642  break;
643  case IDOK:
644  {
645  context->RebootAfter = GetDlgItemInt(hwndDlg, IDC_RESTARTCOMPAFTER, NULL, FALSE) * 1000 * 60;
646 
647  if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE)) == BST_CHECKED)
648  PhMoveReference(&context->RebootMessage, PhGetWindowText(GetDlgItem(hwndDlg, IDC_RESTARTMESSAGE)));
649  else
650  PhMoveReference(&context->RebootMessage, NULL);
651 
652  context->Dirty = TRUE;
653 
654  EndDialog(hwndDlg, IDOK);
655  }
656  break;
658  {
659  PPH_STRING message;
660  PWSTR computerName;
661  ULONG bufferSize;
662  BOOLEAN allocated = TRUE;
663 
664  // Get the computer name.
665 
666  bufferSize = 64;
667  computerName = PhAllocate((bufferSize + 1) * sizeof(WCHAR));
668 
669  if (!GetComputerName(computerName, &bufferSize))
670  {
671  PhFree(computerName);
672  computerName = PhAllocate((bufferSize + 1) * sizeof(WCHAR));
673 
674  if (!GetComputerName(computerName, &bufferSize))
675  {
676  PhFree(computerName);
677  computerName = L"(unknown)";
678  allocated = FALSE;
679  }
680  }
681 
682  // This message is exactly the same as the one in the Services console,
683  // except the double spaces are replaced by single spaces.
684  message = PhFormatString(
685  L"Your computer is connected to the computer named %s. "
686  L"The %s service on %s has ended unexpectedly. "
687  L"%s will restart automatically, and then you can reestablish the connection.",
688  computerName,
689  context->ServiceItem->Name->Buffer,
690  computerName,
691  computerName
692  );
693  SetDlgItemText(hwndDlg, IDC_RESTARTMESSAGE, message->Buffer);
694  PhDereferenceObject(message);
695 
696  if (allocated)
697  PhFree(computerName);
698 
699  Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE), BST_CHECKED);
700  }
701  break;
702  case IDC_RESTARTMESSAGE:
703  {
704  if (HIWORD(wParam) == EN_CHANGE)
705  {
706  // A zero length restart message disables it, so we might as well uncheck the box.
707  Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE),
708  GetWindowTextLength(GetDlgItem(hwndDlg, IDC_RESTARTMESSAGE)) != 0 ? BST_CHECKED : BST_UNCHECKED);
709  }
710  }
711  break;
712  }
713  }
714  break;
715  }
716 
717  return FALSE;
718 }