Настройка параметров восстановления для служб Windows - PullRequest
15 голосов
/ 16 июня 2011

Недавно я написал небольшой класс, чтобы помочь мне изменить параметры восстановления в службе Windows (большую часть кода я где-то нашел в Интернете). Код создает действие FailureAction для первого, второго и последующих сбоев. Каждый объект Failure содержит тип (None, Restart, Reboot, RunCommand) и Delay (int) в миллисекундах. Эти объекты упаковываются внутри структуры и передаются в 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;
        }
    }
}

Тревор

Ответы [ 2 ]

21 голосов
/ 28 сентября 2012

Команда 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

1 голос
/ 25 февраля 2012

Я обнаружил, что все версии win 7 не имеют возможности перезапустить некоторые сервисы, даже если для их перезапуска в случае сбоя задано значение yes.

Я закончил тем, что написал сервис для мониторинга состояния запуска и остановки, а также реагирования (на основе эвристики) (например, завис, не завис).Хотите, чтобы этот сервисный код был размещен здесь?

Редактировать: Добавлен код

Хорошо, ниже приведен код сервиса, на который я ссылался.Имейте в виду:

  • Этот код предназначен для указания "C: \ appMon" для ведения журнала и утилиты процессора.пороговые значения.
  • Этот код безобразен тем, что его провернули, когда я столкнулся с другими приоритетами - в результате его можно переписать для обработки любого количества служб, пользовательских журналов и путей cfg и т. д..
  • Служба, для которой она была написана, является пресловутым диспетчером очереди Windows (spoolsv.exe).

Чтобы установить службу, используйте installutil.exe от MS.Убедитесь, что служба работает как «локальная системная учетная запись», иначе она не сможет запускать / останавливать службы.

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()
        {
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...