在 Windows 服务上设置恢复选项

问题描述 投票:0回答:3

我最近编写了一个小课程来帮助我更改 Windows 服务的恢复选项(我在网上某处找到的大部分代码)。该代码为第一次、第二次和后续失败创建一个 FailureAction。每个 Failure 对象都包含一个类型(None、Restart、Reboot、RunCommand)和一个以毫秒为单位的延迟 (int)。这些对象打包在结构中并传递到 ChangeServiceConfig2 (WinAPI P/Invoke)。但是,当我实际右键单击控制台上的服务并转到“恢复”选项卡时,您只能为所有故障(第一次、第二次和后续)设置一次延迟(“重启服务器”字段)。当我以编程方式设置它时,它会延迟第一个 FailureAction 并忽略所有其他操作。有谁知道为什么会这样?当只使用第一个时,为什么我们必须为所有 FailureAction 对象传递一个延迟值?我是不是误会了什么?

此外,设置 dwResetPeriod/"Reset fail count after" 似乎没有任何效果。

代码:

public class ServiceConfigurator
{
    private const int SERVICE_ALL_ACCESS = 0xF01FF;
    private const int SC_MANAGER_ALL_ACCESS = 0xF003F;
    private const int SERVICE_CONFIG_DESCRIPTION = 0x1;
    private const int SERVICE_CONFIG_FAILURE_ACTIONS = 0x2;
    private const int SERVICE_NO_CHANGE = -1;
    private const int ERROR_ACCESS_DENIED = 5;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct SERVICE_FAILURE_ACTIONS
    {
        public int dwResetPeriod;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpRebootMsg;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpCommand;
        public int cActions;
        public IntPtr lpsaActions;
    }

    [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]
    private static extern bool ChangeServiceFailureActions(IntPtr hService, int dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS lpInfo);
    [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]
    private static extern bool ChangeServiceDescription(IntPtr hService, int dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_DESCRIPTION lpInfo);

    [DllImport("kernel32.dll")]
    private static extern int GetLastError();

    private IntPtr _ServiceHandle;
    public IntPtr ServiceHandle { get { return _ServiceHandle; } }

    public ServiceConfigurator(ServiceController svcController)
    {
        this._ServiceHandle = svcController.ServiceHandle.DangerousGetHandle();
    }

    public void SetRecoveryOptions(FailureAction pFirstFailure, FailureAction pSecondFailure, FailureAction pSubsequentFailures, int pDaysToResetFailureCount = 0)
    {
        int NUM_ACTIONS = 3;
        int[] arrActions = new int[NUM_ACTIONS * 2];
        int index = 0;
        arrActions[index++] = (int)pFirstFailure.Type;
        arrActions[index++] = pFirstFailure.Delay;
        arrActions[index++] = (int)pSecondFailure.Type;
        arrActions[index++] = pSecondFailure.Delay;
        arrActions[index++] = (int)pSubsequentFailures.Type;
        arrActions[index++] = pSubsequentFailures.Delay;

        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;
        }
    }
}

特雷弗

c# windows-services
3个回答
28
投票

sc 命令提供了一种自动化服务管理的好方法。要从代码中调用只需执行

Process.Start("sc", "args")
并在需要时重定向输出。

这一行告诉服务在等待 1 分钟后重新启动两次。失败时。一天过去后,它会重置失败计数。您还可以将其设置为在后续失败时运行程序等。

sc failure myservice reset= 86400 actions= restart/60000/restart/60000//

http://technet.microsoft.com/en-us/library/cc742019(v=ws.10).aspx


4
投票

我发现所有类型的 win 7 都无法重启某些服务,即使将它们在失败时重启设置为是。

我最终编写了一个服务来监视启动和停止状态以及(启发式派生的)响应能力(例如挂起而不是挂起)。您想要在此处发布该服务代码吗?

编辑:添加代码

好的,下面是我参考的服务代码。请记住:

  • 此代码旨在指向“C: ppMon”进行日志记录和 cpu util。阈值。
  • 这段代码很丑陋,因为它是在我被其他优先事项猛烈抨击时突然出现的——因此,它可以被重写以处理任意数量的服务、用户定义的日志和 cfg 路径等。
  • 它所针对的服务是臭名昭著的 Windows 假脱机程序 (spoolsv.exe)。

要安装该服务,请使用 MS 中的 installutil.exe。确保该服务作为“本地系统帐户”运行,否则将无法启动/停止服务。

appMon.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Timers;
using System.Windows.Forms;
using System.IO;
using System.Net.Mail;
using System.Threading;
using System.Management;

namespace appMon
{
    public class appMon : ServiceBase
    {
        public const string serviceName = "appMon";     
        public appMon()
        {
            InitializeComponent();          
        }       
        private void InitializeComponent()
        {
            this.ServiceName = serviceName;         
        }       
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            // free instantiated object resources not handled by garbage collector
            base.Dispose(disposing);
        }
        public static string getCurrUser()
        {// gets the owner of explorer.exe/UI to determine current logged in user
            String User = String.Empty;
            String Domain = String.Empty;
            String OwnerSID = String.Empty;
            string processname = String.Empty;
            int PID = Process.GetProcessesByName("explorer")[0].Id;
            ObjectQuery sq = new ObjectQuery
                ("Select * from Win32_Process Where ProcessID = '" + PID + "'");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(sq);            
            foreach (ManagementObject oReturn in searcher.Get())
            {
                string[] o = new String[2];
                oReturn.InvokeMethod("GetOwner", (object[])o);
                User = o[0];
                System.IO.StreamWriter sr = new System.IO.StreamWriter(@"C:\user.txt");
                sr.WriteLine("\\" + o[2] + "\\" + o[1] + "\\" + o[0]);
                return User;
            }
            return User;
        }
        public static int readConfigFile()
        {
            int cputime = 5; // 5 min dflt
            try
            {
                string readcfg;
                readcfg = File.ReadAllText(@"c:\appMon\cpuUtilization.txt");
                cputime = Convert.ToInt16(readcfg);
                return cputime;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
                return cputime;  // 5 min dflt
            }
        }
        public static void logEvents(bool spoolerHang, bool appHang, string msg)
        {
            try
            {
                StreamWriter sw;
                sw = File.AppendText(@"c:\appMon\appMonLog.txt");
                sw.WriteLine(@"appMon spoolsv.exe event: " + "," + System.Environment.MachineName + "," + System.DateTime.Now + "," + msg);
                sw.Close();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }
        /// <summary>
        /// Start this service.
        /// </summary> 
        protected override void OnStart(string[] args)
        {// upon appMon load, a polling interval is set (in milliseconds 1000 ms = 1 s)
            System.Timers.Timer pollTimer = new System.Timers.Timer();
            pollTimer.Elapsed += new ElapsedEventHandler(pollTimer_Elapsed);
            pollTimer.Interval = 20000; // 20 sec
            pollTimer.Enabled = true;
        }
        public static void StartService(string serviceName, int timeoutMilliseconds)
        {
            ServiceController service = new ServiceController(serviceName);
            try
            {
                TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
                if (service.Status == ServiceControllerStatus.Stopped) // if service is not running...
                {
                    service.Start(); // ...start the service
                }
            }
            catch(Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }
        public static void StopService(string serviceName, int timeoutMilliseconds)
        {
            ServiceController service = new ServiceController(serviceName);
            try
            {
                TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
                if (service.Status == ServiceControllerStatus.Running) // if service is running...
                {
                    service.Stop(); //...stop the service
                }
                service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }
        public static void delFiles(string path)
        {
            string[] filePaths = Directory.GetFiles(path);
            foreach (string filePath in filePaths)
            {
                try
                {
                    File.Delete(filePath);
                }
                catch (Exception e)
                {
                    // TODO:  !log to file instead!
                    MessageBox.Show("error deleting files: " + e.ToString());
                }
            }
        }
        public static void getServiceInfo(string serviceName)
        {
            ServiceController service = new ServiceController(serviceName);
            ServiceController[] depServices = service.ServicesDependedOn;
            List<string> depServicesList = new List<string>();
            foreach (ServiceController sc in depServices)
            {
                depServicesList.Add(sc.ServicesDependedOn.ToString());
                logEvents(false, false, sc.ServicesDependedOn.ToString());
            }

        }
        void pollTimer_Elapsed(object sender, ElapsedEventArgs e)
        {// polling interval has elapsed            
            getServiceInfo("spooler");
            ServiceController serviceSpooler = new ServiceController("spooler");
            if (serviceSpooler.Status == ServiceControllerStatus.Stopped)
            {
                logEvents(true, false, "Print Spooler is: " + serviceSpooler.Status.ToString());
                serviceSpooler.Refresh();
                serviceSpooler.Start();
                logEvents(true, false, "Print Spooler is: " + serviceSpooler.Status.ToString());
            }            
            int cputime = readConfigFile();
            // get active processes (exe's, including services)
            Process[] processlist = Process.GetProcesses();
            // iterate through process list
            foreach (Process theprocess in processlist)
            {
                // assign local variable to iterator - cures the foreach "gotcha"
                Process p = theprocess;
                if (p.ProcessName == "spoolsv") // "spoolsv" = windows name for spoolsv.exe aka "spooler"
                {
                    if (p.TotalProcessorTime.Minutes > cputime) // has current spooler thread occupied >= cputime # mins of CPU time? 
                    {
                        logEvents(true, false, "spoolsv.exe CPU time (mins): " + p.TotalProcessorTime.Minutes.ToString());
                        p.Refresh();
                        StopService("spooler", 0);
                        StartService("spooler", 0);
                    }
                }
            }
        }
        /// <summary>
        /// Stop this service.
        /// </summary>
        /// 
        protected override void OnStop()
        {
        }
    }
}

0
投票

您可以通过获取恢复选项的二进制字符串值,使用 Windows 注册表选项添加 Windows 服务恢复选项。 如果您在安装服务的 Windows 注册表中查看,则关键 FailureActions 用于设置具有二进制值的恢复选项。 [如果您检查图像,它具有我从 Windows 注册表中获取的二进制值,您的服务将安装在

Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\serviceName

中的路径

1

检查以下代码以编程方式设置 Windows 恢复选项。 您也可以将

ManagementClass
用于 Windows 注册表。

string strFailureActionBinary = "00-00-00-00-00-00-00-00-00-00-00-00-03-00-00-00-14-00-00-00-01-00-00-00-60-EA-00-00-01-00-00-00-60-EA-00-00-01-00-00-00-60-EA-00-00";
string[] arrFailureActionBinary = strFailureActionBinary.Split(new string[]
{
    "-"
}, StringSplitOptions.None);
var mcReg = new ManagementClass(_managementScopeForRegistry, new ManagementPath("StdRegProv"), _objectGetOptions);
var inparamsReg = mcReg.GetMethodParameters("SetBinaryValue");
inparamsReg["hDefKey"] = 0x80000002; // HKEY_LOCAL_MACHINE;
inparamsReg["sSubKeyName"] = "SYSTEM\\CurrentControlSet\\Services\\" + serviceName;
inparamsReg["sValueName"] = "FailureActions";
inparamsReg["uValue"] = arrFailureActionBinary.Select(x => Convert.ToInt32(x, 16)).ToArray(); //its expect value in array
mcReg.InvokeMethod("SetBinaryValue", inparamsReg, null);
© www.soinside.com 2019 - 2024. All rights reserved.