public unsafe void ChangeServiceConfig2_ErrorsProperly() { Assert.False(ChangeServiceConfig2(SafeServiceHandle.Null, ServiceInfoLevel.SERVICE_CONFIG_DESCRIPTION, null)); string rebootMessage = "Rebooting now."; fixed(char *pRebootMessage = rebootMessage.ToCharArrayWithNullTerminator()) { var actions = new SC_ACTION[] { new SC_ACTION { Type = SC_ACTION_TYPE.SC_ACTION_REBOOT, Delay = 2000 }, new SC_ACTION { Type = SC_ACTION_TYPE.SC_ACTION_REBOOT, Delay = 2000 }, }; fixed(SC_ACTION *pActions = &actions[0]) { var failureActions = new SERVICE_FAILURE_ACTIONS { lpRebootMsg = pRebootMessage, cActions = 2, lpsaActions = pActions, }; Assert.False(ChangeServiceConfig2(SafeServiceHandle.Null, ServiceInfoLevel.SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &failureActions)); } } }
private void SetFailureActions(IntPtr serviceHandle) { UInt32 dwBytesNeeded; // Determine the buffer size needed QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, IntPtr.Zero, 0, out dwBytesNeeded); var ptr = Marshal.AllocHGlobal((int)dwBytesNeeded); QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, ptr, dwBytesNeeded, out dwBytesNeeded); var failureActions = new SERVICE_FAILURE_ACTIONS(); Marshal.PtrToStructure(ptr, failureActions); // Report it. ResetPeriod = (UInt32)failureActions.dwResetPeriod; FailureProgram = failureActions.lpCommand; var offset = 0; for (var i = 0; i < failureActions.cActions; i++) { var type = (FailureAction)Marshal.ReadInt32(failureActions.lpsaActions, offset); offset += sizeof(Int32); var delay = (UInt32)Marshal.ReadInt32(failureActions.lpsaActions, offset); offset += sizeof(Int32); if (i == 0) { FirstFailure = type; } else if (i == 1) { SecondFailure = type; } else if (i == 2) { ThirdFailure = type; } switch (type) { case (FailureAction.RunCommand): RunCommandDelay = delay; break; case (FailureAction.Reboot): RebootDelay = delay; break; case (FailureAction.Restart): RestartDelay = delay; break; } } Marshal.FreeHGlobal(ptr); }
/// <summary> /// Changes the service failure action information /// </summary> /// <param name="serviceName">The service to look for</param> /// <param name="resetPeriodInDays">The reset period (in days)</param> /// <param name="rebootMessage">The message displayed on reboot</param> /// <param name="commandLine">Program command line</param> /// <param name="actions">Ordered list of actions to add to the service configuration</param> public static void SetServiceInformation(string serviceName, int resetPeriodInDays, string rebootMessage, string commandLine, ServiceFailureActionType[] actions) { // Get a valid service handle IntPtr serviceHandle = LookupServiceHandle(serviceName, scope.Modify); // marshal the actions ServiceFailureAction action = new ServiceFailureAction(); IntPtr lpsaActions = Marshal.AllocHGlobal(Marshal.SizeOf(action) * actions.Length); // Marshal.StructureToPtr(action, lpsaActions, false); IntPtr nextAction = lpsaActions; for (int i = 0; i < actions.Length; i++) { action = new ServiceFailureAction(); action.Type = actions[i]; action.Delay = (UInt32)TimeSpan.FromMinutes(1).TotalMilliseconds; Marshal.StructureToPtr(action, nextAction, false); nextAction = (IntPtr)(nextAction.ToInt64() + Marshal.SizeOf(action)); } // now put it all in one struct SERVICE_FAILURE_ACTIONS failureActions = new SERVICE_FAILURE_ACTIONS(); failureActions.dwResetPeriod = (int)TimeSpan.FromDays(resetPeriodInDays).TotalSeconds; failureActions.lpRebootMsg = rebootMessage; failureActions.lpCommand = commandLine; failureActions.cActions = actions.Length; failureActions.lpsaActions = lpsaActions; IntPtr lpInfo = Marshal.AllocHGlobal(Marshal.SizeOf(failureActions)); Marshal.StructureToPtr(failureActions, lpInfo, true); // do the change bool success = ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, lpInfo); //int errorcode = GetLatError(); // clean up Marshal.FreeHGlobal(lpInfo); Marshal.FreeHGlobal(lpsaActions); if (serviceHandle != IntPtr.Zero) { CloseServiceHandle(serviceHandle); } if (false == success) { throw new System.Runtime.InteropServices.ExternalException(string.Format("Cannot set ServiceConfig. Last Error: {0}.", Marshal.GetLastWin32Error())); } }
public static void SetRecoveryOptions(String serviceName, int action = SC_ACTION_RESTART, int pDaysToResetFailureCount = 0) { ServiceController svcController = new ServiceController(serviceName); IntPtr _ServiceHandle = svcController.ServiceHandle.DangerousGetHandle(); int NUM_ACTIONS = 3; int[] arrActions = new int[NUM_ACTIONS * 2]; int index = 0; arrActions[index++] = action; arrActions[index++] = DELAY_IN_MILLISECONDS; arrActions[index++] = action; arrActions[index++] = DELAY_IN_MILLISECONDS; arrActions[index++] = action; arrActions[index++] = DELAY_IN_MILLISECONDS; IntPtr tmpBuff = Marshal.AllocHGlobal(NUM_ACTIONS * 8); try { Marshal.Copy(arrActions, 0, tmpBuff, NUM_ACTIONS * 2); SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS(); sfa.cActions = 3; sfa.dwResetPeriod = pDaysToResetFailureCount; sfa.lpCommand = null; sfa.lpRebootMsg = null; sfa.lpsaActions = new IntPtr(tmpBuff.ToInt32()); bool success = ChangeServiceFailureActions(_ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa); if (!success) { if (GetLastError() == ERROR_ACCESS_DENIED) { throw new Exception("Access denied while setting failure actions."); } else { throw new Exception("Unknown error while setting failure actions."); } } } finally { Marshal.FreeHGlobal(tmpBuff); tmpBuff = IntPtr.Zero; } }
/// <summary> /// Configures the service to auto-restart on failure /// </summary> public void SetRestartOnFailure(int restartAttempts, int restartDelay, int resetFailuresDelay) { SC_ACTION[] actions = new SC_ACTION[3] { new SC_ACTION { Delay = Math.Max(0, Math.Min(int.MaxValue, restartDelay)), Type = SC_ACTION_TYPE.SC_ACTION_RESTART }, new SC_ACTION { Delay = Math.Max(0, Math.Min(int.MaxValue, restartDelay)), Type = SC_ACTION_TYPE.SC_ACTION_RESTART }, new SC_ACTION { Delay = Math.Max(0, Math.Min(int.MaxValue, restartDelay)), Type = SC_ACTION_TYPE.SC_ACTION_RESTART }, }; for (int i = Math.Max(0, restartAttempts); i < actions.Length; i++) { actions[i] = new SC_ACTION { Delay = 0, Type = SC_ACTION_TYPE.SC_ACTION_NONE } } ; GCHandle hdata = GCHandle.Alloc(actions, GCHandleType.Pinned); try { SERVICE_FAILURE_ACTIONS cfg = new SERVICE_FAILURE_ACTIONS(); cfg.dwResetPeriod = Math.Max(-1, Math.Min(int.MaxValue, resetFailuresDelay)); cfg.lpRebootMsg = cfg.lpCommand = IntPtr.Zero; cfg.cActions = actions.Length; cfg.lpsaActions = hdata.AddrOfPinnedObject(); SetServiceConfig(SERVICE_CONFIG_INFO.FAILURE_ACTIONS, cfg); } finally { hdata.Free(); } }
/// <summary> /// Look up the Failure action information for a service /// </summary> /// <param name="serviceName">The service to look up</param> /// <returns>Service Failure Action information</returns> public static ServiceInformation GetSericeInformation(string serviceName) { UInt32 dwBytesNeeded; // Get a valid service handle IntPtr serviceHandle = LookupServiceHandle(serviceName, scope.Query); // Determine the buffer size needed bool sucess = QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, IntPtr.Zero, 0, out dwBytesNeeded); IntPtr ptr = Marshal.AllocHGlobal((int)dwBytesNeeded); sucess = QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, ptr, dwBytesNeeded, out dwBytesNeeded); if (false == sucess) { throw new System.Runtime.InteropServices.ExternalException(string.Format("Cannot find SERVICE_FAILURE_ACTIONS struct. Last Error: {0}.", Marshal.GetLastWin32Error())); } SERVICE_FAILURE_ACTIONS failureActions = new SERVICE_FAILURE_ACTIONS(); Marshal.PtrToStructure(ptr, failureActions); ServiceInformation serviceConfigInformation = new ServiceInformation((UInt32)failureActions.dwResetPeriod, failureActions.lpRebootMsg, failureActions.lpCommand, failureActions.cActions); int offset = 0; for (int i = 0; i < failureActions.cActions; i++) { ServiceFailureAction action = new ServiceFailureAction(); action.Type = (ServiceFailureActionType)Marshal.ReadInt32(failureActions.lpsaActions, offset); offset += sizeof(Int32); action.Delay = (UInt32)Marshal.ReadInt32(failureActions.lpsaActions, offset); offset += sizeof(Int32); serviceConfigInformation.Actions[i] = action; } // clean up Marshal.FreeHGlobal(ptr); if (serviceHandle != IntPtr.Zero) { CloseServiceHandle(serviceHandle); } return(serviceConfigInformation); }
public static void SetRecoveryOptions(String serviceName, int pDaysToResetFailureCount = 0) { ServiceController svcController = new ServiceController(serviceName); IntPtr _ServiceHandle = svcController.ServiceHandle.DangerousGetHandle(); int NUM_ACTIONS = 3; int[] arrActions = new int[NUM_ACTIONS * 2]; int index = 0; arrActions[index++] = SC_ACTION_RESTART; arrActions[index++] = DELAY_IN_MILLISECONDS; arrActions[index++] = (int)SC_ACTION_RESTART; arrActions[index++] = DELAY_IN_MILLISECONDS; arrActions[index++] = (int)SC_ACTION_RESTART; arrActions[index++] = DELAY_IN_MILLISECONDS; IntPtr tmpBuff = Marshal.AllocHGlobal(NUM_ACTIONS * 8); try { Marshal.Copy(arrActions, 0, tmpBuff, NUM_ACTIONS * 2); SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS(); sfa.cActions = 3; sfa.dwResetPeriod = pDaysToResetFailureCount; sfa.lpCommand = null; sfa.lpRebootMsg = null; sfa.lpsaActions = new IntPtr(tmpBuff.ToInt32()); bool success = ChangeServiceFailureActions(_ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa); if (!success) { if (GetLastError() == ERROR_ACCESS_DENIED) throw new Exception("Access denied while setting failure actions."); else throw new Exception("Unknown error while setting failure actions."); } } finally { Marshal.FreeHGlobal(tmpBuff); tmpBuff = IntPtr.Zero; } }
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); }
internal static extern bool ChangeServiceConfig2(SafeHandle serviceHandle, SERVICE_CONFIG_INFOLEVEL infoLevel, ref SERVICE_FAILURE_ACTIONS serviceDesc);
private bool SetRestartOnFailureRecovery(List <FailureAction> FailureActions, String RunProgram, String RebootMessage) { // We've got work to do IntPtr scmHndl = IntPtr.Zero; IntPtr svcHndl = IntPtr.Zero; IntPtr tmpBuf = IntPtr.Zero; IntPtr svcLock = IntPtr.Zero; // Err check var bool rslt = false; // Place all our code in a try block try { // Open the service control manager scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS); if (scmHndl.ToInt32() <= 0) { EventLog.WriteEntry(serviceInstaller.ServiceName, Properties.Resources.ScmHndlInfo, EventLogEntryType.Error); return(false); } // Lock the Service Database svcLock = LockServiceDatabase(scmHndl); if (svcLock.ToInt32() <= 0) { EventLog.WriteEntry(serviceInstaller.ServiceName, Properties.Resources.SvcLockInfo, EventLogEntryType.Error); return(false); } // Open the service svcHndl = OpenService(scmHndl, serviceInstaller.ServiceName, SERVICE_ALL_ACCESS); if (svcHndl.ToInt32() <= 0) { EventLog.WriteEntry(serviceInstaller.ServiceName, Properties.Resources.SvcHndlInfo, EventLogEntryType.Error); return(false); } // 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. // Bill is aware of this and has promised no fixes. Also note that the API allows // granularity of seconds whereas GUI only shows days and minutes. // We're gonna serialize the SA_ACTION structs into an array of ints // for simplicity in marshalling this variable length ptr to win32 int[] actions = new int[FailureActions.Count * 2]; int currInd = 0; foreach (FailureAction fa in FailureActions) { actions[currInd] = (int)fa.Type; actions[++currInd] = fa.Delay; currInd++; } // Need to pack 8 bytes per struct tmpBuf = Marshal.AllocHGlobal(FailureActions.Count * 8); // Move array into marshallable pointer Marshal.Copy(actions, 0, tmpBuf, FailureActions.Count * 2); // Set the SERVICE_FAILURE_ACTIONS struct SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS(); sfa.cActions = FailureActions.Count; sfa.dwResetPeriod = SERVICE_NO_CHANGE; sfa.lpCommand = RunProgram; sfa.lpRebootMsg = RebootMessage; sfa.lpsaActions = tmpBuf.ToInt32(); // Call the ChangeServiceFailureActions() abstraction of ChangeServiceConfig2() rslt = ChangeServiceFailureActions(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa); //Check the return if (!rslt) { int err = GetLastError(); if (err == ERROR_ACCESS_DENIED) { throw new Exception(Properties.Resources.AccessDenied); } } // Free the memory Marshal.FreeHGlobal(tmpBuf); tmpBuf = IntPtr.Zero; EventLog.WriteEntry(serviceInstaller.ServiceName, Properties.Resources.Successfully, EventLogEntryType.Information); } catch (Exception ex) { EventLog.WriteEntry(serviceInstaller.ServiceName, ex.Message, EventLogEntryType.Error); return(false); } finally { if (scmHndl != IntPtr.Zero) { // Unlock the service database if (svcLock != IntPtr.Zero) { UnlockServiceDatabase(svcLock); svcLock = IntPtr.Zero; } // Close the service control manager handle 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; } } return(true); }
/// <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 void SetServiceRecoveryOptions( string serviceName, [NotNull] ServiceRecoveryOptions recoveryOptions) { if (recoveryOptions == null) { throw new ArgumentNullException("recoveryOptions"); } bool requiresShutdownPriveleges = recoveryOptions.FirstFailureAction == ServiceRecoveryAction.RestartComputer || recoveryOptions.SecondFailureAction == ServiceRecoveryAction.RestartComputer || recoveryOptions.SubsequentFailureAction == ServiceRecoveryAction.RestartComputer; if (requiresShutdownPriveleges) { GrantShutdownPrivileges(); } int actionCount = 3; var restartServiceAfter = (uint)TimeSpan.FromMinutes( recoveryOptions.RestartServiceWaitMinutes).TotalMilliseconds; IntPtr failureActionsPointer = IntPtr.Zero; IntPtr actionPointer = IntPtr.Zero; ServiceController controller = null; try { // Open the service controller = new ServiceController(serviceName); // Set up the failure actions var failureActions = new SERVICE_FAILURE_ACTIONS(); failureActions.dwResetPeriod = (int)TimeSpan.FromDays(recoveryOptions.ResetFailureCountWaitDays).TotalSeconds; failureActions.cActions = (uint)actionCount; failureActions.lpRebootMsg = recoveryOptions.RestartSystemMessage; // allocate memory for the individual actions actionPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SC_ACTION)) * actionCount); ServiceRecoveryAction[] actions = { recoveryOptions.FirstFailureAction, recoveryOptions.SecondFailureAction, recoveryOptions.SubsequentFailureAction }; for (int i = 0; i < actions.Length; i++) { ServiceRecoveryAction action = actions[i]; SC_ACTION scAction = GetScAction(action, restartServiceAfter); Marshal.StructureToPtr(scAction, (IntPtr)((Int64)actionPointer + (Marshal.SizeOf(typeof(SC_ACTION))) * i), false); } failureActions.lpsaActions = actionPointer; string command = recoveryOptions.RunProgramCommand; if (command != null) { failureActions.lpCommand = command; } failureActionsPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_FAILURE_ACTIONS))); Marshal.StructureToPtr(failureActions, failureActionsPointer, false); // Make the change bool success = ChangeServiceConfig2(controller.ServiceHandle.DangerousGetHandle(), SERVICE_CONFIG_FAILURE_ACTIONS, failureActionsPointer); // Check that the change occurred if (!success) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to change the Service configuration."); } } finally { if (failureActionsPointer != IntPtr.Zero) { Marshal.FreeHGlobal(failureActionsPointer); } if (actionPointer != IntPtr.Zero) { Marshal.FreeHGlobal(actionPointer); } if (controller != null) { controller.Dispose(); } //log.Debug(m => m("Done setting service recovery options.")); } }
public void InstallService(string serviceName, string serviceDisplayName, string logonAccount, string logonPassword) { Trace.Entering(); string agentServiceExecutable = Path.Combine(IOUtil.GetBinPath(), WindowsServiceControlManager.WindowsServiceControllerName); IntPtr scmHndl = IntPtr.Zero; IntPtr svcHndl = IntPtr.Zero; IntPtr tmpBuf = IntPtr.Zero; IntPtr svcLock = IntPtr.Zero; try { Trace.Verbose(StringUtil.Format("Trying to open SCManager.")); scmHndl = OpenSCManager(null, null, ServiceManagerRights.AllAccess); if (scmHndl.ToInt64() <= 0) { throw new Exception(StringUtil.Loc("FailedToOpenSCM")); } Trace.Verbose(StringUtil.Format("Opened SCManager. Trying to create service {0}", serviceName)); svcHndl = CreateService(scmHndl, serviceName, serviceDisplayName, ServiceRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, agentServiceExecutable, null, IntPtr.Zero, null, logonAccount, logonPassword); if (svcHndl.ToInt64() <= 0) { throw new InvalidOperationException(StringUtil.Loc("OperationFailed", nameof(CreateService), GetLastError())); } //invoke the service with special argument, that tells it to register an event log trace source (need to run as an admin) using (var processInvoker = HostContext.CreateService <IProcessInvoker>()) { processInvoker.ExecuteAsync(string.Empty, agentServiceExecutable, "init", null, default(System.Threading.CancellationToken)).GetAwaiter().GetResult(); } _term.WriteLine(StringUtil.Loc("ServiceInstalled", serviceName)); //set recovery option to restart on failure. ArrayList failureActions = new ArrayList(); //first failure, we will restart the service right away. failureActions.Add(new FailureAction(RecoverAction.Restart, 0)); //second failure, we will restart the service after 1 min. failureActions.Add(new FailureAction(RecoverAction.Restart, 60000)); //subsequent failures, we will restart the service after 1 min failureActions.Add(new FailureAction(RecoverAction.Restart, 60000)); // Lock the Service Database svcLock = LockServiceDatabase(scmHndl); if (svcLock.ToInt64() <= 0) { throw new Exception(StringUtil.Loc("FailedToLockServiceDB")); } int[] actions = new int[failureActions.Count * 2]; int currInd = 0; foreach (FailureAction fa in failureActions) { actions[currInd] = (int)fa.Type; actions[++currInd] = fa.Delay; currInd++; } // Need to pack 8 bytes per struct tmpBuf = Marshal.AllocHGlobal(failureActions.Count * 8); // Move array into marshallable pointer Marshal.Copy(actions, 0, tmpBuf, failureActions.Count * 2); // Set the SERVICE_FAILURE_ACTIONS struct SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS(); sfa.cActions = failureActions.Count; sfa.dwResetPeriod = SERVICE_NO_CHANGE; sfa.lpCommand = String.Empty; sfa.lpRebootMsg = String.Empty; sfa.lpsaActions = tmpBuf.ToInt64(); // Call the ChangeServiceFailureActions() abstraction of ChangeServiceConfig2() bool result = ChangeServiceFailureActions(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa); //Check the return if (!result) { int lastErrorCode = (int)GetLastError(); Exception win32exception = new Win32Exception(lastErrorCode); if (lastErrorCode == ReturnCode.ERROR_ACCESS_DENIED) { throw new SecurityException(StringUtil.Loc("AccessDeniedSettingRecoveryOption"), win32exception); } else { throw win32exception; } } else { _term.WriteLine(StringUtil.Loc("ServiceRecoveryOptionSet", serviceName)); } _term.WriteLine(StringUtil.Loc("ServiceConfigured", serviceName)); } finally { if (scmHndl != IntPtr.Zero) { // Unlock the service database if (svcLock != IntPtr.Zero) { UnlockServiceDatabase(svcLock); svcLock = IntPtr.Zero; } // Close the service control manager handle 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 void InstallAndStart(string serviceName, string displayName, string fileName) { IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess); if (service == IntPtr.Zero) service = CreateService(scm, serviceName, displayName, ServiceAccessRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, fileName, null, IntPtr.Zero, null, null, null); if (service == IntPtr.Zero) throw new ApplicationException("Failed to install service."); // set restart options try { SC_ACTION action = new SC_ACTION(); action.Type = SC_ACTION_TYPE.RestartService; action.Delay = (uint)TimeSpan.FromSeconds(10).TotalMilliseconds; IntPtr lpsaActions = Marshal.AllocHGlobal(Marshal.SizeOf(action) * 2); if (lpsaActions == IntPtr.Zero) { throw new Exception(String.Format("Unable to allocate memory for service action, error was: 0x{0:X}", Marshal.GetLastWin32Error())); } Marshal.StructureToPtr(action, lpsaActions, false); IntPtr nextAction = (IntPtr)(lpsaActions.ToInt64() + Marshal.SizeOf(action)); action.Type = SC_ACTION_TYPE.RestartService; Marshal.StructureToPtr(action, nextAction, false); action.Delay = (uint)TimeSpan.FromSeconds(30).TotalMilliseconds; SERVICE_FAILURE_ACTIONS failureActions = new SERVICE_FAILURE_ACTIONS(); failureActions.dwResetPeriod = (int)TimeSpan.FromDays(1).TotalSeconds; failureActions.lpRebootMsg = ""; failureActions.lpCommand = ""; failureActions.cActions = 2; failureActions.lpsaActions = lpsaActions; IntPtr lpInfo = Marshal.AllocHGlobal(Marshal.SizeOf(failureActions)); if (lpInfo == IntPtr.Zero) { Marshal.FreeHGlobal(lpsaActions); throw new Exception(String.Format("Unable to allocate memory, error was: 0x{0:X}", Marshal.GetLastWin32Error())); } Marshal.StructureToPtr(failureActions, lpInfo, false); if (!ChangeServiceConfig2(service, 2, lpInfo)) { Marshal.FreeHGlobal(lpInfo); Marshal.FreeHGlobal(lpsaActions); int err = Marshal.GetLastWin32Error(); if (err == 1072) { throw new Exception(String.Format("Can't set service config. Service " + serviceName + " is marked for delete. Restart machine to clear this up.", err)); } else { throw new Exception(String.Format("Error setting service config, error was: {0}", err)); } } Marshal.FreeHGlobal(lpInfo); Marshal.FreeHGlobal(lpsaActions); } finally { } try { StartService(service); } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scm); } }
public static ServiceRecoveryOptions GetServiceRecoveryOptions( string serviceName) { ThrowHelper.ThrowArgumentNullIfNull(serviceName, "serviceName"); ThrowHelper.ThrowArgumentOutOfRangeIfEmpty(serviceName, "serviceName"); log.Debug(m => m("Getting service recovery options...")); // 8KB is the largest buffer supported by QueryServiceConfig2 const int bufferSize = 1024 * 8; IntPtr bufferPtr = IntPtr.Zero; ServiceRecoveryOptions recoveryOptions; ServiceController controller = null; try { // Open the service controller = new ServiceController(serviceName); uint dwBytesNeeded; // Allocate memory for struct bufferPtr = Marshal.AllocHGlobal(bufferSize); int queryResult = QueryServiceConfig2( controller.ServiceHandle.DangerousGetHandle(), SERVICE_CONFIG_FAILURE_ACTIONS, bufferPtr, bufferSize, out dwBytesNeeded); if (queryResult == 0) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to query the Service configuration."); } // Cast the buffer to a QUERY_SERVICE_CONFIG struct SERVICE_FAILURE_ACTIONS config = (SERVICE_FAILURE_ACTIONS)Marshal.PtrToStructure(bufferPtr, typeof(SERVICE_FAILURE_ACTIONS)); recoveryOptions = new ServiceRecoveryOptions(); recoveryOptions.DaysToResetFailAcount = (int) TimeSpan.FromSeconds(config.dwResetPeriod).TotalDays; recoveryOptions.RebootMessage = config.lpRebootMsg; recoveryOptions.CommandToLaunchOnFailure = config.lpCommand; int actionCount = config.cActions; if (actionCount != 0) { uint millisecondsToRestartService = 0; SC_ACTION[] actions = new SC_ACTION[actionCount]; for (int i = 0; i < config.cActions; i++) { SC_ACTION action = (SC_ACTION)Marshal.PtrToStructure( (IntPtr)(config.lpsaActions.ToInt32() + (Marshal.SizeOf(typeof(SC_ACTION)) * i)), typeof(SC_ACTION)); actions[i] = action; millisecondsToRestartService = action.Delay; } recoveryOptions.FirstFailureAction = getServiceRecoveryAction(actions[0]); recoveryOptions.SecondFailureAction = getServiceRecoveryAction(actions[1]); recoveryOptions.SubsequentFailureActions = getServiceRecoveryAction(actions[2]); recoveryOptions.MinutesToRestartService = (int)TimeSpan.FromMilliseconds(millisecondsToRestartService).TotalMinutes; } } finally { // Clean up if (bufferPtr != IntPtr.Zero) { Marshal.FreeHGlobal(bufferPtr); } if (controller != null) { controller.Close(); } log.Debug(m => m("Done getting service recovery options.")); } return(recoveryOptions); }
/// <summary> /// Look up the Failure action information for a service /// </summary> /// <param name="serviceName">The service to look up</param> /// <returns>Service Failure Action information</returns> public static ServiceInformation GetSericeInformation(string serviceName) { UInt32 dwBytesNeeded; // Get a valid service handle IntPtr serviceHandle = LookupServiceHandle(serviceName, scope.Query); // Determine the buffer size needed bool sucess = QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, IntPtr.Zero, 0, out dwBytesNeeded); IntPtr ptr = Marshal.AllocHGlobal((int)dwBytesNeeded); sucess = QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, ptr, dwBytesNeeded, out dwBytesNeeded); if (false == sucess) { throw new System.Runtime.InteropServices.ExternalException(string.Format("Cannot find SERVICE_FAILURE_ACTIONS struct. Last Error: {0}.", Marshal.GetLastWin32Error())); } SERVICE_FAILURE_ACTIONS failureActions = new SERVICE_FAILURE_ACTIONS(); Marshal.PtrToStructure(ptr, failureActions); ServiceInformation serviceConfigInformation = new ServiceInformation((UInt32)failureActions.dwResetPeriod, failureActions.lpRebootMsg, failureActions.lpCommand, failureActions.cActions); int offset = 0; for (int i = 0; i < failureActions.cActions; i++) { ServiceFailureAction action = new ServiceFailureAction(); action.Type = (ServiceFailureActionType)Marshal.ReadInt32(failureActions.lpsaActions, offset); offset += sizeof(Int32); action.Delay = (UInt32)Marshal.ReadInt32(failureActions.lpsaActions, offset); offset += sizeof(Int32); serviceConfigInformation.Actions[i] = action; } // clean up Marshal.FreeHGlobal(ptr); if (serviceHandle != IntPtr.Zero) { CloseServiceHandle(serviceHandle); } return serviceConfigInformation; }
public static void InstallAndStart(string serviceName, string displayName, string fileName) { IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess); if (service == IntPtr.Zero) { service = CreateService(scm, serviceName, displayName, ServiceAccessRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, fileName, null, IntPtr.Zero, null, null, null); } if (service == IntPtr.Zero) { throw new ApplicationException("Failed to install service."); } // set restart options try { SC_ACTION action = new SC_ACTION(); action.Type = SC_ACTION_TYPE.RestartService; action.Delay = (uint)TimeSpan.FromSeconds(10).TotalMilliseconds; IntPtr lpsaActions = Marshal.AllocHGlobal(Marshal.SizeOf(action) * 2); if (lpsaActions == IntPtr.Zero) { throw new Exception(String.Format("Unable to allocate memory for service action, error was: 0x{0:X}", Marshal.GetLastWin32Error())); } Marshal.StructureToPtr(action, lpsaActions, false); IntPtr nextAction = (IntPtr)(lpsaActions.ToInt64() + Marshal.SizeOf(action)); action.Type = SC_ACTION_TYPE.RestartService; Marshal.StructureToPtr(action, nextAction, false); action.Delay = (uint)TimeSpan.FromSeconds(30).TotalMilliseconds; SERVICE_FAILURE_ACTIONS failureActions = new SERVICE_FAILURE_ACTIONS(); failureActions.dwResetPeriod = (int)TimeSpan.FromDays(1).TotalSeconds; failureActions.lpRebootMsg = ""; failureActions.lpCommand = ""; failureActions.cActions = 2; failureActions.lpsaActions = lpsaActions; IntPtr lpInfo = Marshal.AllocHGlobal(Marshal.SizeOf(failureActions)); if (lpInfo == IntPtr.Zero) { Marshal.FreeHGlobal(lpsaActions); throw new Exception(String.Format("Unable to allocate memory, error was: 0x{0:X}", Marshal.GetLastWin32Error())); } Marshal.StructureToPtr(failureActions, lpInfo, false); if (!ChangeServiceConfig2(service, 2, lpInfo)) { Marshal.FreeHGlobal(lpInfo); Marshal.FreeHGlobal(lpsaActions); int err = Marshal.GetLastWin32Error(); if (err == 1072) { throw new Exception(String.Format("Can't set service config. Service " + serviceName + " is marked for delete. Restart machine to clear this up.", err)); } else { throw new Exception(String.Format("Error setting service config, error was: {0}", err)); } } Marshal.FreeHGlobal(lpInfo); Marshal.FreeHGlobal(lpsaActions); } finally { } try { StartService(service); } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scm); } }
//////////////////////////////////////////////////////////////////////////////////// // The worker method to set all the extension properties for the service private void UpdateServiceConfig(object sender, InstallEventArgs e) { // Determine if we need to set fail actions this.setFailActions = false; int numActions = FailureActions.Count; if (numActions > 0) { setFailActions = true; } // Do we need to do any work that the base installer did not do already? if (!(this.setDescription || this.setFailActions)) return; // We've got work to do IntPtr scmHndl = IntPtr.Zero; IntPtr svcHndl = IntPtr.Zero; IntPtr tmpBuf = IntPtr.Zero; IntPtr svcLock = IntPtr.Zero; // Err check var bool rslt = false; // Place all our code in a try block try { // Open the service control manager scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS); if (scmHndl.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Error, logMsgBase + "Failed to Open Service Control Manager"); return; } // Lock the Service Database svcLock = LockServiceDatabase(scmHndl); if (svcLock.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Error, logMsgBase + "Failed to Lock Service Database for Write"); return; } // Open the service svcHndl = OpenService(scmHndl, base.ServiceName, SERVICE_ALL_ACCESS); if (svcHndl.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Information, logMsgBase + "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. // Bill is aware of this and has promised no fixes. Also note that the API allows // granularity of seconds whereas GUI only shows days and minutes. if (this.setFailActions) { // We're gonna serialize the SA_ACTION structs into an array of ints // for simplicity in marshalling this variable length ptr to win32 int[] actions = new int[numActions * 2]; int currInd = 0; bool needShutdownPrivilege = false; foreach (FailureAction fa in FailureActions) { actions[currInd] = (int)fa.Type; actions[++currInd] = fa.Delay; currInd++; if (fa.Type == RecoverAction.Reboot) { needShutdownPrivilege = true; } } // If we need shutdown privilege, then grant it to this process if (needShutdownPrivilege) { rslt = this.GrandShutdownPrivilege(); if (!rslt) return; } // Need to pack 8 bytes per struct tmpBuf = Marshal.AllocHGlobal(numActions * 8); // Move array into marshallable pointer Marshal.Copy(actions, 0, tmpBuf, numActions * 2); // Set the SERVICE_FAILURE_ACTIONS struct SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS(); sfa.cActions = numActions; sfa.dwResetPeriod = this.failResetTime; sfa.lpCommand = this.failRunCommand; sfa.lpRebootMsg = this.failRebootMsg; sfa.lpsaActions = tmpBuf.ToInt32(); // Call the ChangeServiceFailureActions() abstraction of ChangeServiceConfig2() rslt = ChangeServiceFailureActions(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa); //Check the return if (!rslt) { int err = Marshal.GetLastWin32Error(); if (err == ERROR_ACCESS_DENIED) { throw new Exception(logMsgBase + "Access Denied while setting Failure Actions"); } } // Free the memory Marshal.FreeHGlobal(tmpBuf); tmpBuf = IntPtr.Zero; LogInstallMessage(EventLogEntryType.Information, logMsgBase + "Successfully configured Failure Actions"); } // Need to set the description field? if (this.setDescription) { SERVICE_DESCRIPTION sd = new SERVICE_DESCRIPTION(); sd.lpDescription = this.description; // Call the ChangeServiceDescription() abstraction of ChangeServiceConfig2() rslt = ChangeServiceDescription(svcHndl, SERVICE_CONFIG_DESCRIPTION, ref sd); // Error setting description? if (!rslt) { throw new Exception(logMsgBase + "Failed to set description"); } LogInstallMessage(EventLogEntryType.Information, logMsgBase + "Successfully set description"); } } // Catch all exceptions catch (Exception ex) { ex.Trace(); } finally { if (scmHndl != IntPtr.Zero) { // Unlock the service database if (svcLock != IntPtr.Zero) { UnlockServiceDatabase(svcLock); svcLock = IntPtr.Zero; } // Close the service control manager handle 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; } } // End try-catch-finally }
public static extern bool ChangeServiceConfig2A( int hService, InfoLevel dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS lpInfo);
public static extern int ChangeServiceFailureActions( Win32ServiceControlHandle hService, INFO_LEVEL dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS lpInfo);
public static void SetServiceRecoveryOptions( string serviceName, [NotNull] ServiceRecoveryOptions recoveryOptions) { if (recoveryOptions == null) throw new ArgumentNullException("recoveryOptions"); bool requiresShutdownPriveleges = recoveryOptions.FirstFailureAction == ServiceRecoveryAction.RestartComputer || recoveryOptions.SecondFailureAction == ServiceRecoveryAction.RestartComputer || recoveryOptions.SubsequentFailureAction == ServiceRecoveryAction.RestartComputer; if (requiresShutdownPriveleges) GrantShutdownPrivileges(); int actionCount = 3; var restartServiceAfter = (uint)TimeSpan.FromMinutes( recoveryOptions.RestartServiceWaitMinutes).TotalMilliseconds; IntPtr failureActionsPointer = IntPtr.Zero; IntPtr actionPointer = IntPtr.Zero; ServiceController controller = null; try { // Open the service controller = new ServiceController(serviceName); // Set up the failure actions var failureActions = new SERVICE_FAILURE_ACTIONS(); failureActions.dwResetPeriod = (int)TimeSpan.FromDays(recoveryOptions.ResetFailureCountWaitDays).TotalSeconds; failureActions.cActions = (uint)actionCount; failureActions.lpRebootMsg = recoveryOptions.RestartSystemMessage; // allocate memory for the individual actions actionPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SC_ACTION))*actionCount); ServiceRecoveryAction[] actions = { recoveryOptions.FirstFailureAction, recoveryOptions.SecondFailureAction, recoveryOptions.SubsequentFailureAction }; for (int i = 0; i < actions.Length; i++) { ServiceRecoveryAction action = actions[i]; SC_ACTION scAction = GetScAction(action, restartServiceAfter); Marshal.StructureToPtr(scAction, (IntPtr)((Int64)actionPointer + (Marshal.SizeOf(typeof(SC_ACTION)))*i), false); } failureActions.lpsaActions = actionPointer; string command = recoveryOptions.RunProgramCommand; if (command != null) failureActions.lpCommand = command; failureActionsPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_FAILURE_ACTIONS))); Marshal.StructureToPtr(failureActions, failureActionsPointer, false); // Make the change bool success = ChangeServiceConfig2(controller.ServiceHandle.DangerousGetHandle(), SERVICE_CONFIG_FAILURE_ACTIONS, failureActionsPointer); // Check that the change occurred if (!success) throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to change the Service configuration."); } finally { if (failureActionsPointer != IntPtr.Zero) Marshal.FreeHGlobal(failureActionsPointer); if (actionPointer != IntPtr.Zero) Marshal.FreeHGlobal(actionPointer); if (controller != null) { controller.Dispose(); } //log.Debug(m => m("Done setting service recovery options.")); } }
ChangeServiceFailureActions(IntPtr hService, int dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS lpInfo);
public static extern bool ChangeServiceFailureActions(IntPtr hService, int dwInfoLevel, ref SERVICE_FAILURE_ACTIONS lpInfo);
public static void ServiceQuery(string host, string svcname) { bool success; IntPtr dbHandle; IntPtr svcHandle; IntPtr ptr; UInt32 dwBytesNeeded; QUERY_SERVICE_CONFIG qconf; SERVICE_FAILURE_ACTIONS qfail; int failStructOffset; QUERY_SERVICE_STATUS_PROCESS qproc; if (host == "") { dbHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS); } else { dbHandle = OpenSCManager(host, null, SC_MANAGER_ALL_ACCESS); } svcHandle = OpenService(dbHandle, svcname, SERVICE_QUERY_CONFIG); if (svcHandle == IntPtr.Zero) { throw new System.Runtime.InteropServices.ExternalException("Couldn't open the service"); } ptr = Marshal.AllocHGlobal(4096); success = QueryServiceConfig(svcHandle, ptr, 4096, out dwBytesNeeded); if (success == false) { throw new System.Runtime.InteropServices.ExternalException("Couldn't query the service"); } qconf = new QUERY_SERVICE_CONFIG(); // copy ptr to query_service_config struct Marshal.PtrToStructure(ptr, qconf); Marshal.FreeHGlobal(ptr); //reset states success = false; dwBytesNeeded = 0; success = QueryServiceConfig2(svcHandle, SERVICE_CONFIG_FAILURE_ACTIONS, IntPtr.Zero, 0, out dwBytesNeeded); //the first call of this function will get the number of bytes needed for the struct ptr = Marshal.AllocHGlobal((int)dwBytesNeeded); success = QueryServiceConfig2(svcHandle, SERVICE_CONFIG_FAILURE_ACTIONS, ptr, dwBytesNeeded, out dwBytesNeeded); qfail = new SERVICE_FAILURE_ACTIONS(); Marshal.PtrToStructure(ptr, qfail); SC_ACTION[] actions = new SC_ACTION[qfail.cActions]; failStructOffset = 0; Marshal.FreeHGlobal(ptr); //reset states success = false; dwBytesNeeded = 0; success = QueryServiceStatusEx(svcHandle, SC_STATUS_PROCESS_INFO, IntPtr.Zero, 0, out dwBytesNeeded);//again first call gets buffer size required for struct ptr = Marshal.AllocHGlobal((int)dwBytesNeeded); success = QueryServiceStatusEx(svcHandle, SC_STATUS_PROCESS_INFO, ptr, dwBytesNeeded, out dwBytesNeeded); qproc = new QUERY_SERVICE_STATUS_PROCESS(); Marshal.PtrToStructure(ptr, qproc); Marshal.FreeHGlobal(ptr); //reset states success = false; dwBytesNeeded = 0; for (int i = 0; i < qfail.cActions; i++) { SC_ACTION act = new SC_ACTION(); act.type = Marshal.ReadInt32(qfail.lpsaActions, failStructOffset); failStructOffset += sizeof(Int32); act.dwDelay = (UInt32)Marshal.ReadInt32(qfail.lpsaActions, failStructOffset); failStructOffset += sizeof(Int32); actions[i] = act; } Console.WriteLine("Service Name:\t" + svcname); Console.WriteLine("Service Type:\t" + qproc.dwServiceType); Console.WriteLine("State:\t" + qproc.dwCurrentState); Console.WriteLine("PID:\t" + qproc.dwProcessId); Console.WriteLine(); Console.WriteLine("Start Type:\t" + qconf.dwStartType); Console.WriteLine("Error Control:\t" + qconf.dwErrorControl); Console.WriteLine("BinPath:\t" + qconf.lpBinaryPathName); Console.WriteLine("Display Name:\t" + qconf.lpDisplayName); Console.WriteLine("Dependencies:\t" + qconf.lpDependencies); Console.WriteLine("Reset Period:\t" + qfail.dwResetPeriod); Console.WriteLine("Reboot Message:\t" + qfail.lpRebootMsg); Console.WriteLine("Failure Command Line:\t" + qfail.lpCommand); Console.WriteLine("Failure Actions:\t"); for (int i = 0; i < actions.Length; i++) { Console.Write("{0} -- Delay = {1}", actions[i].type, actions[i].dwDelay); } }
//////////////////////////////////////////////////////////////////////////////////// // The worker method to set all the extension properties for the service private void UpdateServiceConfig(object sender, InstallEventArgs e) { // Determine if we need to set fail actions this.setFailActions = false; int numActions = FailureActions.Count; if (numActions > 0) { setFailActions = true; } // Do we need to do any work that the base installer did not do already? if (!(this.setDescription || this.setFailActions)) { return; } // We've got work to do IntPtr scmHndl = IntPtr.Zero; IntPtr svcHndl = IntPtr.Zero; IntPtr tmpBuf = IntPtr.Zero; IntPtr svcLock = IntPtr.Zero; // Err check var bool rslt = false; // Place all our code in a try block try { // Open the service control manager scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS); if (scmHndl.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Error, logMsgBase + "Failed to Open Service Control Manager"); return; } // Lock the Service Database svcLock = LockServiceDatabase(scmHndl); if (svcLock.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Error, logMsgBase + "Failed to Lock Service Database for Write"); return; } // Open the service svcHndl = OpenService(scmHndl, base.ServiceName, SERVICE_ALL_ACCESS); if (svcHndl.ToInt32() <= 0) { LogInstallMessage(EventLogEntryType.Information, logMsgBase + "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. // Bill is aware of this and has promised no fixes. Also note that the API allows // granularity of seconds whereas GUI only shows days and minutes. if (this.setFailActions) { // We're gonna serialize the SA_ACTION structs into an array of ints // for simplicity in marshalling this variable length ptr to win32 int[] actions = new int[numActions * 2]; int currInd = 0; bool needShutdownPrivilege = false; foreach (FailureAction fa in FailureActions) { actions[currInd] = (int)fa.Type; actions[++currInd] = fa.Delay; currInd++; if (fa.Type == RecoverAction.Reboot) { needShutdownPrivilege = true; } } // If we need shutdown privilege, then grant it to this process if (needShutdownPrivilege) { rslt = this.GrandShutdownPrivilege(); if (!rslt) { return; } } // Need to pack 8 bytes per struct tmpBuf = Marshal.AllocHGlobal(numActions * 8); // Move array into marshallable pointer Marshal.Copy(actions, 0, tmpBuf, numActions * 2); // Set the SERVICE_FAILURE_ACTIONS struct SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS(); sfa.cActions = numActions; sfa.dwResetPeriod = this.failResetTime; sfa.lpCommand = this.failRunCommand; sfa.lpRebootMsg = this.failRebootMsg; sfa.lpsaActions = tmpBuf.ToInt32(); // Call the ChangeServiceFailureActions() abstraction of ChangeServiceConfig2() rslt = ChangeServiceFailureActions(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa); //Check the return if (!rslt) { int err = GetLastError(); if (err == ERROR_ACCESS_DENIED) { throw new Exception(logMsgBase + "Access Denied while setting Failure Actions"); } } // Free the memory Marshal.FreeHGlobal(tmpBuf); tmpBuf = IntPtr.Zero; LogInstallMessage(EventLogEntryType.Information, logMsgBase + "Successfully configured Failure Actions"); } // Need to set the description field? if (this.setDescription) { SERVICE_DESCRIPTION sd = new SERVICE_DESCRIPTION(); sd.lpDescription = this.description; // Call the ChangeServiceDescription() abstraction of ChangeServiceConfig2() rslt = ChangeServiceDescription(svcHndl, SERVICE_CONFIG_DESCRIPTION, ref sd); // Error setting description? if (!rslt) { throw new Exception(logMsgBase + "Failed to set description"); } LogInstallMessage(EventLogEntryType.Information, logMsgBase + "Successfully set description"); } } // Catch all exceptions catch (Exception ex) { LogInstallMessage(EventLogEntryType.Error, ex.Message); } finally{ if (scmHndl != IntPtr.Zero) { // Unlock the service database if (svcLock != IntPtr.Zero) { UnlockServiceDatabase(svcLock); svcLock = IntPtr.Zero; } // Close the service control manager handle 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; } } // End try-catch-finally }
public static void SetServiceRecoveryOptions( string serviceName, ServiceRecoveryOptions recoveryOptions) { Exceptions.ThrowHelper.ThrowArgumentNullIfNull(serviceName, "serviceName"); Exceptions.ThrowHelper.ThrowArgumentOutOfRangeIfEmpty(serviceName, "serviceName"); Log.Debug("Setting service recovery options..."); bool requiresShutdownPriveleges = recoveryOptions.FirstFailureAction == ServiceRecoveryAction.RestartTheComputer || recoveryOptions.SecondFailureAction == ServiceRecoveryAction.RestartTheComputer || recoveryOptions.SubsequentFailureActions == ServiceRecoveryAction.RestartTheComputer; if (requiresShutdownPriveleges) { GrantShutdownPrivileges(); } const int actionCount = 3; var restartServiceAfter = (uint)TimeSpan.FromMinutes( recoveryOptions.MinutesToRestartService).TotalMilliseconds; IntPtr failureActionsPointer = IntPtr.Zero; IntPtr actionPointer = IntPtr.Zero; ServiceController controller = null; try { // Open the service controller = new ServiceController(serviceName); // Set up the failure actions var failureActions = new SERVICE_FAILURE_ACTIONS { dwResetPeriod = (int)TimeSpan.FromDays(recoveryOptions.DaysToResetFailAcount).TotalSeconds, cActions = actionCount, lpRebootMsg = recoveryOptions.RebootMessage }; // allocate memory for the individual actions actionPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SC_ACTION)) * actionCount); ServiceRecoveryAction[] actions = { recoveryOptions.FirstFailureAction, recoveryOptions.SecondFailureAction, recoveryOptions.SubsequentFailureActions }; for (int i = 0; i < actions.Length; i++) { ServiceRecoveryAction action = actions[i]; var scAction = GetScAction(action, restartServiceAfter); Marshal.StructureToPtr(scAction, (IntPtr)((Int64)actionPointer + (Marshal.SizeOf(typeof(SC_ACTION))) * i), false); } failureActions.lpsaActions = actionPointer; string command = recoveryOptions.CommandToLaunchOnFailure; if (command != null) { failureActions.lpCommand = command; } failureActionsPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_FAILURE_ACTIONS))); Marshal.StructureToPtr(failureActions, failureActionsPointer, false); // Make the change bool success = ChangeServiceConfig2( controller.ServiceHandle.DangerousGetHandle(), SERVICE_CONFIG_FAILURE_ACTIONS, failureActionsPointer); // Check that the change occurred if (!success) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to change the Service configuration."); } } finally { if (failureActionsPointer != IntPtr.Zero) { Marshal.FreeHGlobal(failureActionsPointer); } if (actionPointer != IntPtr.Zero) { Marshal.FreeHGlobal(actionPointer); } if (controller != null) { controller.Close(); } Log.Debug("Done setting service recovery options."); } }
void SetRestartOnFailure(string serviceName) { const int actionCount = 2; const uint delay = 60000; var service = IntPtr.Zero; var failureActionsPtr = IntPtr.Zero; var actionPtr = IntPtr.Zero; try { service = OpenService(serviceName, SERVICE_ACCESS_RIGHTS.SERVICE_CHANGE_CONFIG | SERVICE_ACCESS_RIGHTS.SERVICE_START); actionPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SC_ACTION)) * actionCount); var action1 = new SC_ACTION { Type = SC_ACTION_TYPE.SC_ACTION_RESTART, Delay = delay }; Marshal.StructureToPtr(action1, actionPtr, false); var action2 = new SC_ACTION { Type = SC_ACTION_TYPE.SC_ACTION_NONE, Delay = delay }; Marshal.StructureToPtr(action2, (IntPtr)((long)actionPtr + Marshal.SizeOf(typeof(SC_ACTION))), false); var failureActions = new SERVICE_FAILURE_ACTIONS { dwResetPeriod = 0, cActions = actionCount, lpsaActions = actionPtr }; failureActionsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_FAILURE_ACTIONS))); Marshal.StructureToPtr(failureActions, failureActionsPtr, false); var changeResult = ChangeServiceConfig2( service, ServiceConfig2InfoLevel.SERVICE_CONFIG_FAILURE_ACTIONS, failureActionsPtr); if (changeResult == 0) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to change the Service configuration."); } } finally { // Clean up if (failureActionsPtr != IntPtr.Zero) { Marshal.FreeHGlobal(failureActionsPtr); } if (actionPtr != IntPtr.Zero) { Marshal.FreeHGlobal(actionPtr); } if (service != IntPtr.Zero) { CloseServiceHandle(service); } } }
public static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_INFO_LEVEL dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS lpInfo);