public static void SetRecoveryOptions(IntPtr service, SC_ACTION[] actions, bool applyOnErrorStop = false, uint failureResetSeconds = 3600, string command = null, string rebootMsg = null) { var recovery = new SERVICE_FAILURE_ACTIONS(); var defaultAction = new SC_ACTION(SC_ACTION_TYPE.SC_ACTION_NONE, 0); recovery.cActions = 3; var size = Marshal.SizeOf(typeof(SC_ACTION)); recovery.lpsaActions = Marshal.AllocHGlobal((System.Int32)(size * recovery.cActions)); for (int ix = 0; ix < recovery.cActions; ++ix) { var action = (actions != null && ix < actions.Length) ? actions[ix] : defaultAction; Marshal.StructureToPtr(action, IntPtr.Add(recovery.lpsaActions, ix * size), false); } recovery.dwResetPeriod = failureResetSeconds; // time to reset failure counter, seconds recovery.lpCommand = command; // command to execute recovery.lpRebootMsg = rebootMsg; // reboot message (we don't use it) try { if (!ChangeServiceConfig2(service, SERVICE_INFO_LEVEL.FAILURE_ACTIONS, ref recovery)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } finally { // clean up so no memory leak // Note, does nothing when given IntPtr.Zero Marshal.FreeHGlobal(recovery.lpsaActions); } // Set whether or not to apply recovery actions if service stops itself with non-0 Win32ExitCode var failure_actions_flag = new SERVICE_FAILURE_ACTIONS_FLAG(); failure_actions_flag.fFailureActionsOnNonCrashFailures = applyOnErrorStop; ChangeServiceConfig2(service, SERVICE_INFO_LEVEL.FAILURE_ACTIONS_FLAG, ref failure_actions_flag); }
/// <summary> /// Update installed service's config accordinly. /// </summary> /// <param name="sender">sender</param> /// <param name="e">event args</param> private void UpdateServiceConfig(object sender, InstallEventArgs e) { IntPtr scmHndl = IntPtr.Zero; IntPtr svcHndl = IntPtr.Zero; IntPtr tmpBuf = IntPtr.Zero; IntPtr svcLock = IntPtr.Zero; try { // Open the service control manager scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS); if (scmHndl.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Error, "Failed to Open Service Control Manager"); return; } // Lock the Service Database svcLock = LockServiceDatabase(scmHndl); if (svcLock.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Error, "Failed to Lock Service Database for Write"); return; } // Open the service svcHndl = OpenService(scmHndl, ServiceName, SERVICE_ALL_ACCESS); if (svcHndl.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Information, "Failed to Open Service "); return; } // Need to set service failure actions. Note that the API lets us set as many as // we want, yet the Service Control Manager GUI only lets us see the first 3. // Also note that the API allows granularity of seconds whereas GUI only shows days and minutes. if (FailureActions.Count > 0) { int[] actions = new int[FailureActions.Count * 2]; int i = 0; bool needShutdownPrivilege = false; foreach (FailureAction fa in FailureActions) { actions[i] = (int)fa.Type; actions[++i] = fa.DelaySeconds * 1000; i++; if (fa.Type == FailureActionType.Reboot) { needShutdownPrivilege = true; } } // If we need shutdown privilege, then grant it to this process if (needShutdownPrivilege) { GrantShutdownPrivilege(); } tmpBuf = Marshal.AllocHGlobal(FailureActions.Count * 8); Marshal.Copy(actions, 0, tmpBuf, FailureActions.Count * 2); SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS(); sfa.cActions = FailureActions.Count; sfa.dwResetPeriod = FailCountResetTime; if (FailureCommandAction != null) { sfa.lpCommand = FailureCommandAction.ToString(); } sfa.lpRebootMsg = FailureRebootMessage; sfa.lpsaActions = tmpBuf.ToInt32(); SERVICE_FAILURE_ACTIONS_FLAG sfaf = new SERVICE_FAILURE_ACTIONS_FLAG(); sfaf.bFailureAction = true; if (!ChangeServiceFailureActionFlag(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, ref sfaf)) { throw new Win32Exception(GetLastError()); } if (!ChangeServiceFailureActions(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa)) { if (GetLastError() == ERROR_ACCESS_DENIED) { throw new Exception( "Access Denied while setting Failure Actions"); } } Marshal.FreeHGlobal(tmpBuf); tmpBuf = IntPtr.Zero; LogInstallMessage(EventLogEntryType.Information, "Successfully configured Failure Actions"); } if (Description != null && Description.Length > 0) { SERVICE_DESCRIPTION sd = new SERVICE_DESCRIPTION(); sd.lpDescription = Description; if (!ChangeServiceDescription(svcHndl, SERVICE_CONFIG_DESCRIPTION, ref sd)) { throw new Exception("Failed to set description"); } LogInstallMessage(EventLogEntryType.Information, "Successfully set description"); } } catch (Exception ex) { LogInstallMessage(EventLogEntryType.Error, Format(ex)); } finally { if (scmHndl != IntPtr.Zero) { // Unlock the service database if (svcLock != IntPtr.Zero) { UnlockServiceDatabase(svcLock); svcLock = IntPtr.Zero; } CloseServiceHandle(scmHndl); scmHndl = IntPtr.Zero; } // Close the service handle if (svcHndl != IntPtr.Zero) { CloseServiceHandle(svcHndl); svcHndl = IntPtr.Zero; } // Free the memory if (tmpBuf != IntPtr.Zero) { Marshal.FreeHGlobal(tmpBuf); tmpBuf = IntPtr.Zero; } } }
public static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_INFO_LEVEL dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS_FLAG lpInfo);