我最近编写了一个小课程来帮助我更改 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;
}
}
}
特雷弗
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
我发现所有类型的 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()
{
}
}
}
您可以通过获取恢复选项的二进制字符串值,使用 Windows 注册表选项添加 Windows 服务恢复选项。 如果您在安装服务的 Windows 注册表中查看,则关键 FailureActions 用于设置具有二进制值的恢复选项。 [如果您检查图像,它具有我从 Windows 注册表中获取的二进制值,您的服务将安装在
Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\serviceName
中的路径
检查以下代码以编程方式设置 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);