如何使用C#更改Windows Service Recovery选项

时间:2018-10-26 12:44:56

标签: c# .net windows-services

我有Windows服务上托管的WCF服务。我使用Windows安装程序安装了此服务。有时,当我停止使用C#代码进行服务时,它会停下来。所以我想,如果服务没有在2分钟内停止,为什么不杀死服务。我的代码如下以停止服务:

var service = ServiceController.GetServices()
                .FirstOrDefault(s => s.ServiceName == serviceName);
            try
            {
                if (service == null || service.Status != ServiceControllerStatus.Running) return;
                if(service.CanStop)
                {
                    session.LogInfo($"Stopping '{serviceName}'.");
                    TimeSpan timeout = TimeSpan.FromMilliseconds(ServiceStopTime);
                    service.Stop();
                    service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
                    session.LogInfo($"'{serviceName}' stopped successfully.");
                }

它正在按预期方式工作。如果服务没有停止,我想终止进程。这是我要杀死进程的代码。

var processName = GetProcessNameByWindowsService(serviceName);
            if (processName == null) return;
            Process[] procs = Process.GetProcessesByName(processName);
            if (procs.Length > 0)
            {
                foreach (Process proc in procs)
                {
                    session.LogInfo($"Killing Process'{processName}'.");
                    proc.Kill();
                    session.LogInfo($"'{processName}' killed successfully.");
                }
            }

它也按预期工作,但是问题是当我终止该进程时,该服务没有停止。它为服务和服务保持运行分配新流程。谷歌搜索并投入了一段时间后,我发现了导致该窗口服务恢复选项的原因,如果该服务失败,它将重新启动该服务。我想更改/设置服务的恢复选项,以防万一第一次失败,第二次失败以及随后的失败而无法使用C#代码执行任何操作。我用谷歌搜索,但没有找到任何东西。所以我想知道如何使用C#更改已安装的窗口服务的恢复选项?

1 个答案:

答案 0 :(得分:2)

花费时间后,我终于在this链接的帮助下找到了解决方案。我写了两个帮助程序类来设置/更新Windows服务的恢复选项。首先,我在下面编写了一个静态帮助器类:

using System;
using System.Runtime.InteropServices;

namespace HRTC.CustomActions.Helpers
{
    public static class ServiceRecoveryOptionHelper
    {
        //Action Enum
        public enum RecoverAction
        {
            None = 0, Restart = 1, Reboot = 2, RunCommand = 3
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

        public struct ServiceFailureActions
        {
            public int dwResetPeriod;
            [MarshalAs(UnmanagedType.LPWStr)]

            public string lpRebootMsg;
            [MarshalAs(UnmanagedType.LPWStr)]

            public string lpCommand;
            public int cActions;
            public IntPtr lpsaActions;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class ScAction
        {
            public int type;
            public uint dwDelay;
        }

        // Win32 function to open the service control manager
        [DllImport("advapi32.dll")]
        public static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, int dwDesiredAccess);

        // Win32 function to open a service instance
        [DllImport("advapi32.dll")]
        public static extern IntPtr OpenService(IntPtr hScManager, string lpServiceName, int dwDesiredAccess);

        // Win32 function to change the service config for the failure actions.
        [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]

        public static extern bool ChangeServiceFailureActions(IntPtr hService, int dwInfoLevel,
            [MarshalAs(UnmanagedType.Struct)]
            ref ServiceFailureActions lpInfo);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "QueryServiceConfig2W")]
        public static extern Boolean QueryServiceConfig2(IntPtr hService, UInt32 dwInfoLevel, IntPtr buffer, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);

        [DllImport("kernel32.dll")]
        public static extern int GetLastError();
    }
    public class FailureAction
    {
        // Default constructor
        public FailureAction() { }

        // Constructor
        public FailureAction(ServiceRecoveryOptionHelper.RecoverAction actionType, int actionDelay)
        {
            Type = actionType;
            Delay = actionDelay;
        }

        // Property to set recover action type
        public ServiceRecoveryOptionHelper.RecoverAction Type { get; set; } = ServiceRecoveryOptionHelper.RecoverAction.None;

        // Property to set recover action delay
        public int Delay { get; set; }
    }
}

然后我已经为Windows服务设置了静态类,该类具有不同的方法,例如启动Windows服务,停止Windows服务和安装服务等。我在此类中添加了新的静态方法,以更改Windows服务的恢复选项,该选项接收4个参数。第一个是服务名称,其他三个分别是第一个,第二个和后续恢复选项的恢复选项。下面是它的实现。

using System;
using System.Collections;
using System.Runtime.InteropServices;

namespace HRTC.CustomActions.Helpers
{
    public class LocalServiceHelper
    {
        //Change service recovery option settings
        private const int ServiceAllAccess = 0xF01FF;
        private const int ScManagerAllAccess = 0xF003F;
        private const int ServiceConfigFailureActions = 0x2;
        private const int ErrorAccessDenied = 5;


        public static void ChangeRevoveryOption(string serviceName, ServiceRecoveryOptionHelper.RecoverAction firstFailureAction,
            ServiceRecoveryOptionHelper.RecoverAction secondFailureAction, ServiceRecoveryOptionHelper.RecoverAction thirdFailureAction)
        {
            try
            {
                // Open the service control manager
                var scmHndl = ServiceRecoveryOptionHelper.OpenSCManager(null, null, ScManagerAllAccess);
                if (scmHndl.ToInt32() <= 0)
                    return;

                // Open the service
                var svcHndl = ServiceRecoveryOptionHelper.OpenService(scmHndl, serviceName, ServiceAllAccess);

                if (svcHndl.ToInt32() <= 0)
                    return;

                var failureActions = new ArrayList
                {
                    // First Failure Actions and Delay (msec)
                    new FailureAction(firstFailureAction, 0),
                    // Second Failure Actions and Delay (msec)
                    new FailureAction(secondFailureAction, 0),
                    // Subsequent Failures Actions and Delay (msec)
                    new FailureAction(thirdFailureAction, 0)
                };

                var numActions = failureActions.Count;
                var myActions = new int[numActions * 2];
                var currInd = 0;

                foreach (FailureAction fa in failureActions)
                {
                    myActions[currInd] = (int) fa.Type;
                    myActions[++currInd] = fa.Delay;
                    currInd++;
                }

                // Need to pack 8 bytes per struct
                var tmpBuf = Marshal.AllocHGlobal(numActions * 8);

                // Move array into marshallable pointer
                Marshal.Copy(myActions, 0, tmpBuf, numActions * 2);

                // Set the SERVICE_FAILURE_ACTIONS struct
                var config =
                    new ServiceRecoveryOptionHelper.ServiceFailureActions
                    {
                        cActions = 3,
                        dwResetPeriod = 0,
                        lpCommand = null,
                        lpRebootMsg = null,
                        lpsaActions = new IntPtr(tmpBuf.ToInt32())
                    };

                // Call the ChangeServiceFailureActions() abstraction of ChangeServiceConfig2()
                var result =
                    ServiceRecoveryOptionHelper.ChangeServiceFailureActions(svcHndl, ServiceConfigFailureActions,
                        ref config);

                //Check the return
                if (!result)
                {
                    var err = ServiceRecoveryOptionHelper.GetLastError();
                    if (err == ErrorAccessDenied)
                    {
                        throw new Exception("Access Denied while setting Failure Actions");

                    }

                    // Free the memory
                    Marshal.FreeHGlobal(tmpBuf);
                }
            }
            catch (Exception)
            {
                throw new Exception("Unable to set service recovery options");
            }
        }
    }

}

就是这样。您只需要调用该方法即可更改Windows服务的恢复选项。例如:

LocalServiceHelper.ChangeRevoveryOption("ServiceName",
                    ServiceRecoveryOptionHelper.RecoverAction.Restart,
                    ServiceRecoveryOptionHelper.RecoverAction.Restart,
                    ServiceRecoveryOptionHelper.RecoverAction.None);

它将在调用该方法时更新Windows服务的恢复选项。希望对您有所帮助。祝您满意! :)