如何使用 C# 更改 windows 服务恢复选项
How to change windows service recovery option using c#
我有托管在 windows 服务上的 WCF 服务。我使用 Windows 安装程序安装了此服务。有时,当我使用 C# 代码停止服务时,它会停滞不前。所以我想,如果服务在 2 分钟内没有停止,为什么不终止服务。我的代码如下停止服务:
var service = ServiceController.GetServices()
.FirstOrDefault(s => s.ServiceName == serviceName);
try
{
if (service == null || service.Status != ServiceControllerStatus.Running) return;
if(service.CanStop)
{
session.LogInfo($"Stopping '{serviceName}'.");
TimeSpan timeout = TimeSpan.FromMilliseconds(ServiceStopTime);
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
session.LogInfo($"'{serviceName}' stopped successfully.");
}
它按预期工作。如果服务没有停止,我想终止我的进程。这是我终止进程的代码。
var processName = GetProcessNameByWindowsService(serviceName);
if (processName == null) return;
Process[] procs = Process.GetProcessesByName(processName);
if (procs.Length > 0)
{
foreach (Process proc in procs)
{
session.LogInfo($"Killing Process'{processName}'.");
proc.Kill();
session.LogInfo($"'{processName}' killed successfully.");
}
}
它也按预期工作,但问题是当我终止进程时,服务不会停止。它将新流程分配给服务并让服务保持运行。在谷歌上搜索并投入了一些时间后,我发现原因是 window 服务恢复选项,如果失败则重新启动服务。我想 change/set 服务的恢复选项,以防第一次失败、第二次失败和后续失败使用 C# 代码不采取任何操作。我用谷歌搜索但没有找到任何东西。所以我想知道如何使用 C# 更改已安装 window 服务的恢复选项?
经过投入时间,终于在 this link 的帮助下找到了解决方案。我已经为 windows 服务的 set/update 恢复选项编写了两个助手 classes。首先,我写了一个静态助手 class,如下所示:
using System;
using System.Runtime.InteropServices;
namespace HRTC.CustomActions.Helpers
{
public static class ServiceRecoveryOptionHelper
{
//Action Enum
public enum RecoverAction
{
None = 0, Restart = 1, Reboot = 2, RunCommand = 3
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct ServiceFailureActions
{
public int dwResetPeriod;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpRebootMsg;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpCommand;
public int cActions;
public IntPtr lpsaActions;
}
[StructLayout(LayoutKind.Sequential)]
public class ScAction
{
public int type;
public uint dwDelay;
}
// Win32 function to open the service control manager
[DllImport("advapi32.dll")]
public static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, int dwDesiredAccess);
// Win32 function to open a service instance
[DllImport("advapi32.dll")]
public static extern IntPtr OpenService(IntPtr hScManager, string lpServiceName, int dwDesiredAccess);
// Win32 function to change the service config for the failure actions.
[DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]
public static extern bool ChangeServiceFailureActions(IntPtr hService, int dwInfoLevel,
[MarshalAs(UnmanagedType.Struct)]
ref ServiceFailureActions lpInfo);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "QueryServiceConfig2W")]
public static extern Boolean QueryServiceConfig2(IntPtr hService, UInt32 dwInfoLevel, IntPtr buffer, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);
[DllImport("kernel32.dll")]
public static extern int GetLastError();
}
public class FailureAction
{
// Default constructor
public FailureAction() { }
// Constructor
public FailureAction(ServiceRecoveryOptionHelper.RecoverAction actionType, int actionDelay)
{
Type = actionType;
Delay = actionDelay;
}
// Property to set recover action type
public ServiceRecoveryOptionHelper.RecoverAction Type { get; set; } = ServiceRecoveryOptionHelper.RecoverAction.None;
// Property to set recover action delay
public int Delay { get; set; }
}
}
然后我已经为 windows 服务设置了静态 class,这些服务具有不同的方法,例如启动 windows 服务、停止 windows 服务和安装服务等。我添加了此 class 中的新静态方法可更改接收 4 个参数的 windows 服务的恢复选项。第一个是服务名称,其他三个分别是第一个、第二个和后续恢复选项的恢复选项。下面是它的实现。
using System;
using System.Collections;
using System.Runtime.InteropServices;
namespace HRTC.CustomActions.Helpers
{
public class LocalServiceHelper
{
//Change service recovery option settings
private const int ServiceAllAccess = 0xF01FF;
private const int ScManagerAllAccess = 0xF003F;
private const int ServiceConfigFailureActions = 0x2;
private const int ErrorAccessDenied = 5;
public static void ChangeRevoveryOption(string serviceName, ServiceRecoveryOptionHelper.RecoverAction firstFailureAction,
ServiceRecoveryOptionHelper.RecoverAction secondFailureAction, ServiceRecoveryOptionHelper.RecoverAction thirdFailureAction)
{
try
{
// Open the service control manager
var scmHndl = ServiceRecoveryOptionHelper.OpenSCManager(null, null, ScManagerAllAccess);
if (scmHndl.ToInt32() <= 0)
return;
// Open the service
var svcHndl = ServiceRecoveryOptionHelper.OpenService(scmHndl, serviceName, ServiceAllAccess);
if (svcHndl.ToInt32() <= 0)
return;
var failureActions = new ArrayList
{
// First Failure Actions and Delay (msec)
new FailureAction(firstFailureAction, 0),
// Second Failure Actions and Delay (msec)
new FailureAction(secondFailureAction, 0),
// Subsequent Failures Actions and Delay (msec)
new FailureAction(thirdFailureAction, 0)
};
var numActions = failureActions.Count;
var myActions = new int[numActions * 2];
var currInd = 0;
foreach (FailureAction fa in failureActions)
{
myActions[currInd] = (int) fa.Type;
myActions[++currInd] = fa.Delay;
currInd++;
}
// Need to pack 8 bytes per struct
var tmpBuf = Marshal.AllocHGlobal(numActions * 8);
// Move array into marshallable pointer
Marshal.Copy(myActions, 0, tmpBuf, numActions * 2);
// Set the SERVICE_FAILURE_ACTIONS struct
var config =
new ServiceRecoveryOptionHelper.ServiceFailureActions
{
cActions = 3,
dwResetPeriod = 0,
lpCommand = null,
lpRebootMsg = null,
lpsaActions = new IntPtr(tmpBuf.ToInt32())
};
// Call the ChangeServiceFailureActions() abstraction of ChangeServiceConfig2()
var result =
ServiceRecoveryOptionHelper.ChangeServiceFailureActions(svcHndl, ServiceConfigFailureActions,
ref config);
//Check the return
if (!result)
{
var err = ServiceRecoveryOptionHelper.GetLastError();
if (err == ErrorAccessDenied)
{
throw new Exception("Access Denied while setting Failure Actions");
}
// Free the memory
Marshal.FreeHGlobal(tmpBuf);
}
}
catch (Exception)
{
throw new Exception("Unable to set service recovery options");
}
}
}
}
就是这样。您只需要调用方法来更改 windows 服务的恢复选项。例如:
LocalServiceHelper.ChangeRevoveryOption("ServiceName",
ServiceRecoveryOptionHelper.RecoverAction.Restart,
ServiceRecoveryOptionHelper.RecoverAction.Restart,
ServiceRecoveryOptionHelper.RecoverAction.None);
它将更新 windows 服务的恢复选项,正如您在调用该方法时提到的那样。希望这有帮助。编码快乐! :)
我有托管在 windows 服务上的 WCF 服务。我使用 Windows 安装程序安装了此服务。有时,当我使用 C# 代码停止服务时,它会停滞不前。所以我想,如果服务在 2 分钟内没有停止,为什么不终止服务。我的代码如下停止服务:
var service = ServiceController.GetServices()
.FirstOrDefault(s => s.ServiceName == serviceName);
try
{
if (service == null || service.Status != ServiceControllerStatus.Running) return;
if(service.CanStop)
{
session.LogInfo($"Stopping '{serviceName}'.");
TimeSpan timeout = TimeSpan.FromMilliseconds(ServiceStopTime);
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
session.LogInfo($"'{serviceName}' stopped successfully.");
}
它按预期工作。如果服务没有停止,我想终止我的进程。这是我终止进程的代码。
var processName = GetProcessNameByWindowsService(serviceName);
if (processName == null) return;
Process[] procs = Process.GetProcessesByName(processName);
if (procs.Length > 0)
{
foreach (Process proc in procs)
{
session.LogInfo($"Killing Process'{processName}'.");
proc.Kill();
session.LogInfo($"'{processName}' killed successfully.");
}
}
它也按预期工作,但问题是当我终止进程时,服务不会停止。它将新流程分配给服务并让服务保持运行。在谷歌上搜索并投入了一些时间后,我发现原因是 window 服务恢复选项,如果失败则重新启动服务。我想 change/set 服务的恢复选项,以防第一次失败、第二次失败和后续失败使用 C# 代码不采取任何操作。我用谷歌搜索但没有找到任何东西。所以我想知道如何使用 C# 更改已安装 window 服务的恢复选项?
经过投入时间,终于在 this link 的帮助下找到了解决方案。我已经为 windows 服务的 set/update 恢复选项编写了两个助手 classes。首先,我写了一个静态助手 class,如下所示:
using System;
using System.Runtime.InteropServices;
namespace HRTC.CustomActions.Helpers
{
public static class ServiceRecoveryOptionHelper
{
//Action Enum
public enum RecoverAction
{
None = 0, Restart = 1, Reboot = 2, RunCommand = 3
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct ServiceFailureActions
{
public int dwResetPeriod;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpRebootMsg;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpCommand;
public int cActions;
public IntPtr lpsaActions;
}
[StructLayout(LayoutKind.Sequential)]
public class ScAction
{
public int type;
public uint dwDelay;
}
// Win32 function to open the service control manager
[DllImport("advapi32.dll")]
public static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, int dwDesiredAccess);
// Win32 function to open a service instance
[DllImport("advapi32.dll")]
public static extern IntPtr OpenService(IntPtr hScManager, string lpServiceName, int dwDesiredAccess);
// Win32 function to change the service config for the failure actions.
[DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]
public static extern bool ChangeServiceFailureActions(IntPtr hService, int dwInfoLevel,
[MarshalAs(UnmanagedType.Struct)]
ref ServiceFailureActions lpInfo);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "QueryServiceConfig2W")]
public static extern Boolean QueryServiceConfig2(IntPtr hService, UInt32 dwInfoLevel, IntPtr buffer, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);
[DllImport("kernel32.dll")]
public static extern int GetLastError();
}
public class FailureAction
{
// Default constructor
public FailureAction() { }
// Constructor
public FailureAction(ServiceRecoveryOptionHelper.RecoverAction actionType, int actionDelay)
{
Type = actionType;
Delay = actionDelay;
}
// Property to set recover action type
public ServiceRecoveryOptionHelper.RecoverAction Type { get; set; } = ServiceRecoveryOptionHelper.RecoverAction.None;
// Property to set recover action delay
public int Delay { get; set; }
}
}
然后我已经为 windows 服务设置了静态 class,这些服务具有不同的方法,例如启动 windows 服务、停止 windows 服务和安装服务等。我添加了此 class 中的新静态方法可更改接收 4 个参数的 windows 服务的恢复选项。第一个是服务名称,其他三个分别是第一个、第二个和后续恢复选项的恢复选项。下面是它的实现。
using System;
using System.Collections;
using System.Runtime.InteropServices;
namespace HRTC.CustomActions.Helpers
{
public class LocalServiceHelper
{
//Change service recovery option settings
private const int ServiceAllAccess = 0xF01FF;
private const int ScManagerAllAccess = 0xF003F;
private const int ServiceConfigFailureActions = 0x2;
private const int ErrorAccessDenied = 5;
public static void ChangeRevoveryOption(string serviceName, ServiceRecoveryOptionHelper.RecoverAction firstFailureAction,
ServiceRecoveryOptionHelper.RecoverAction secondFailureAction, ServiceRecoveryOptionHelper.RecoverAction thirdFailureAction)
{
try
{
// Open the service control manager
var scmHndl = ServiceRecoveryOptionHelper.OpenSCManager(null, null, ScManagerAllAccess);
if (scmHndl.ToInt32() <= 0)
return;
// Open the service
var svcHndl = ServiceRecoveryOptionHelper.OpenService(scmHndl, serviceName, ServiceAllAccess);
if (svcHndl.ToInt32() <= 0)
return;
var failureActions = new ArrayList
{
// First Failure Actions and Delay (msec)
new FailureAction(firstFailureAction, 0),
// Second Failure Actions and Delay (msec)
new FailureAction(secondFailureAction, 0),
// Subsequent Failures Actions and Delay (msec)
new FailureAction(thirdFailureAction, 0)
};
var numActions = failureActions.Count;
var myActions = new int[numActions * 2];
var currInd = 0;
foreach (FailureAction fa in failureActions)
{
myActions[currInd] = (int) fa.Type;
myActions[++currInd] = fa.Delay;
currInd++;
}
// Need to pack 8 bytes per struct
var tmpBuf = Marshal.AllocHGlobal(numActions * 8);
// Move array into marshallable pointer
Marshal.Copy(myActions, 0, tmpBuf, numActions * 2);
// Set the SERVICE_FAILURE_ACTIONS struct
var config =
new ServiceRecoveryOptionHelper.ServiceFailureActions
{
cActions = 3,
dwResetPeriod = 0,
lpCommand = null,
lpRebootMsg = null,
lpsaActions = new IntPtr(tmpBuf.ToInt32())
};
// Call the ChangeServiceFailureActions() abstraction of ChangeServiceConfig2()
var result =
ServiceRecoveryOptionHelper.ChangeServiceFailureActions(svcHndl, ServiceConfigFailureActions,
ref config);
//Check the return
if (!result)
{
var err = ServiceRecoveryOptionHelper.GetLastError();
if (err == ErrorAccessDenied)
{
throw new Exception("Access Denied while setting Failure Actions");
}
// Free the memory
Marshal.FreeHGlobal(tmpBuf);
}
}
catch (Exception)
{
throw new Exception("Unable to set service recovery options");
}
}
}
}
就是这样。您只需要调用方法来更改 windows 服务的恢复选项。例如:
LocalServiceHelper.ChangeRevoveryOption("ServiceName",
ServiceRecoveryOptionHelper.RecoverAction.Restart,
ServiceRecoveryOptionHelper.RecoverAction.Restart,
ServiceRecoveryOptionHelper.RecoverAction.None);
它将更新 windows 服务的恢复选项,正如您在调用该方法时提到的那样。希望这有帮助。编码快乐! :)