在Windows服务上设置恢复选项

Tre*_*vor 16 c# windows-services

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

此外,设置dwResetPeriod /"重置失败计数后"似乎没有任何影响.

码:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

特雷弗

Des*_*tar 22

sc命令为自动化服务管理提供了一种很好的方法.要从代码调用,只需执行a Process.Start("sc", "args")并重定向输出,如果您想获得结果.

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

sc failure myservice reset= 86400 actions= restart/60000/restart/60000//
Run Code Online (Sandbox Code Playgroud)

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

  • 如果您要从C#中的Installer []服务安装处理程序调用此函数,则可以将此调用插入到.Committed事件处理程序中,该处理程序将在服务管理器中显示服务后执行它.不要将它放在"AfterInstall"事件管理器中,因为这在第一次在盒子上安装服务时不起作用. (4认同)

小智 4

我发现所有版本的 win 7 都无法重新启动某些服务,即使它们的“失败重启”设置为“是”。

我最终编写了一个服务来监视启动和停止状态以及(启发式派生)响应性(例如挂起而不是挂起)。您希望将服务代码发布在这里吗?

编辑:添加了代码

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

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

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

应用程序Mon.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()
        {
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @negEntropy:欢迎来到 Stack Overflow!如果您想在现有答案中添加某些内容或澄清某一点,请不要打开其他答案。使用编辑功能编辑您现有的答案。请参考这篇文章:http://meta.stackexchange.com/questions/25209/what-is-the-official-etiquette-on-answering-a-question-twice (3认同)