我最近编写了一个小类来帮助我更改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;
}
}
}
特雷弗
答案 0 :(得分:21)
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 :(得分:1)
我发现win 7的所有版本都没有能力重启某些服务,即使它们在失败时重新启动被设置为是。
我最终编写了一项服务来监控启动和停止状态以及(启发式启动)响应(例如挂不挂)。您是否希望在此处发布服务代码?
修改:添加了代码
好的,以下是我提到的服务代码。请记住:
要安装该服务,请使用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()
{
}
}
}