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