2011-06-15 22 views
14

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

此外,设置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; 
     } 
    } 
} 

特雷弗

+0

要为什么只用了只有第一个我假设微软还没有实现的每个故障动作的差异延迟你的问题。尽管只使用第一个结构类型,但对每个结构类型使用一个结构类型可能更容易。此外,服务管理器窗口中的恢复选项卡只有一个条目用于“之后的重新启动服务”,因此它似乎像Windows目前只打算关注第一个。 HTH – Jose 2012-01-26 17:14:58

+0

@threed,此代码缺少FailureAction的定义 – user626528 2014-09-03 07:15:34

+0

@ user626528:恐怕我无法再访问代码(我现在与其他公司有联系)。但据我记忆,'FailureAction'类实质上就是你在示例代码中看到的,它是一个具有两个属性的类:'Type'和'Delay'。 – Trevor 2014-09-03 17:10:26

回答

1

我发现,Win 7的所有口味没有能力重新启动,即使在故障时的重启设置为yes一些服务。我最终编写了一个服务来监视启动和停止状态以及(启发式派生的)响应性(例如挂起不挂起)。你想在这里发布这个服务代码吗?

编辑:添加的代码

好吧,下面是服务代码,我提到。请记住:

  • 此代码旨在指向“C:\ appMon”进行日志记录以及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

当然,那太棒了! – Trevor 2012-02-27 17:59:20

+3

@negEntropy:欢迎使用堆栈溢出!请不要打开额外的答案,如果你想添加一些东西给你现有的答案,或澄清一点。使用编辑功能编辑您现有的答案。请参考这篇文章:http://meta.stackexchange.com/questions/25209/what-is-the-official-etiquette-on-answering-a-question-twice – 2012-02-28 00:24:57

17

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

+3

如果你打算从在C#中安装程序[]服务安装处理程序,您可以将此调用插入到.Committed事件处理程序中,该处理程序将在服务出现在服务管理器中后执行它。不要把它放在“AfterInstall”事件管理器中,因为它在第一次将该服务安装在盒子上时不起作用。 – Contango 2014-03-10 14:45:32

相关问题