public static void SetRecoveryOptions(IntPtr service, SC_ACTION[] actions, bool applyOnErrorStop = false, uint failureResetSeconds = 3600, string command = null, string rebootMsg = null)
        {
            var recovery      = new SERVICE_FAILURE_ACTIONS();
            var defaultAction = new SC_ACTION(SC_ACTION_TYPE.SC_ACTION_NONE, 0);

            recovery.cActions = 3;
            var size = Marshal.SizeOf(typeof(SC_ACTION));

            recovery.lpsaActions = Marshal.AllocHGlobal((System.Int32)(size * recovery.cActions));
            for (int ix = 0; ix < recovery.cActions; ++ix)
            {
                var action = (actions != null && ix < actions.Length) ? actions[ix] : defaultAction;
                Marshal.StructureToPtr(action, IntPtr.Add(recovery.lpsaActions, ix * size), false);
            }
            recovery.dwResetPeriod = failureResetSeconds;   // time to reset failure counter, seconds
            recovery.lpCommand     = command;               // command to execute
            recovery.lpRebootMsg   = rebootMsg;             // reboot message (we don't use it)

            try
            {
                if (!ChangeServiceConfig2(service, SERVICE_INFO_LEVEL.FAILURE_ACTIONS, ref recovery))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                // clean up so no memory leak
                // Note, does nothing when given IntPtr.Zero
                Marshal.FreeHGlobal(recovery.lpsaActions);
            }

            // Set whether or not to apply recovery actions if service stops itself with non-0 Win32ExitCode
            var failure_actions_flag = new SERVICE_FAILURE_ACTIONS_FLAG();

            failure_actions_flag.fFailureActionsOnNonCrashFailures = applyOnErrorStop;
            ChangeServiceConfig2(service, SERVICE_INFO_LEVEL.FAILURE_ACTIONS_FLAG, ref failure_actions_flag);
        }
        /// <summary>
        /// Update installed service's config accordinly.
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">event args</param>
        private void UpdateServiceConfig(object sender, InstallEventArgs e)
        {
            IntPtr scmHndl = IntPtr.Zero;
            IntPtr svcHndl = IntPtr.Zero;
            IntPtr tmpBuf = IntPtr.Zero;
            IntPtr svcLock = IntPtr.Zero;

            try
            {
                // Open the service control manager
                scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
                if (scmHndl.ToInt32() <= 0)
                {
                    LogInstallMessage(EventLogEntryType.Error,
                        "Failed to Open Service Control Manager");
                    return;
                }

                // Lock the Service Database
                svcLock = LockServiceDatabase(scmHndl);
                if (svcLock.ToInt32() <= 0)
                {
                    LogInstallMessage(EventLogEntryType.Error,
                        "Failed to Lock Service Database for Write");
                    return;
                }

                // Open the service
                svcHndl = OpenService(scmHndl, ServiceName, SERVICE_ALL_ACCESS);
                if (svcHndl.ToInt32() <= 0)
                {
                    LogInstallMessage(EventLogEntryType.Information,
                        "Failed to Open Service ");
                    return;
                }

                // Need to set service failure actions. Note that the API lets us set as many as
                // we want, yet the Service Control Manager GUI only lets us see the first 3.
                // Also note that the API allows granularity of seconds whereas GUI only shows days and minutes.
                if (FailureActions.Count > 0)
                {
                    int[] actions = new int[FailureActions.Count * 2];
                    int i = 0;
                    bool needShutdownPrivilege = false;
                    foreach (FailureAction fa in FailureActions)
                    {
                        actions[i] = (int)fa.Type;
                        actions[++i] = fa.DelaySeconds * 1000;
                        i++;
                        if (fa.Type == FailureActionType.Reboot)
                        {
                            needShutdownPrivilege = true;
                        }
                    }
                    // If we need shutdown privilege, then grant it to this process
                    if (needShutdownPrivilege)
                    {
                        GrantShutdownPrivilege();
                    }

                    tmpBuf = Marshal.AllocHGlobal(FailureActions.Count * 8);
                    Marshal.Copy(actions, 0, tmpBuf, FailureActions.Count * 2);
                    SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS();
                    sfa.cActions = FailureActions.Count;
                    sfa.dwResetPeriod = FailCountResetTime;
                    if (FailureCommandAction != null)
                    {
                        sfa.lpCommand = FailureCommandAction.ToString();
                    }
                    sfa.lpRebootMsg = FailureRebootMessage;
                    sfa.lpsaActions = tmpBuf.ToInt32();

                    SERVICE_FAILURE_ACTIONS_FLAG sfaf = new SERVICE_FAILURE_ACTIONS_FLAG();
                    sfaf.bFailureAction = true;
                    if (!ChangeServiceFailureActionFlag(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, ref sfaf))
                    {
                        throw new Win32Exception(GetLastError());
                    }

                    if (!ChangeServiceFailureActions(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa))
                    {
                        if (GetLastError() == ERROR_ACCESS_DENIED)
                        {
                            throw new Exception(
                                "Access Denied while setting Failure Actions");
                        }
                    }

                    Marshal.FreeHGlobal(tmpBuf); tmpBuf = IntPtr.Zero;
                    LogInstallMessage(EventLogEntryType.Information,
                        "Successfully configured Failure Actions");
                }

                if (Description != null && Description.Length > 0)
                {
                    SERVICE_DESCRIPTION sd = new SERVICE_DESCRIPTION();
                    sd.lpDescription = Description;
                    if (!ChangeServiceDescription(svcHndl, SERVICE_CONFIG_DESCRIPTION, ref sd))
                    {
                        throw new Exception("Failed to set description");
                    }
                    LogInstallMessage(EventLogEntryType.Information,
                        "Successfully set description");
                }
            }
            catch (Exception ex)
            {
                LogInstallMessage(EventLogEntryType.Error, Format(ex));
            }
            finally
            {
                if (scmHndl != IntPtr.Zero)
                {
                    // Unlock the service database
                    if (svcLock != IntPtr.Zero)
                    {
                        UnlockServiceDatabase(svcLock);
                        svcLock = IntPtr.Zero;
                    }
                    CloseServiceHandle(scmHndl);
                    scmHndl = IntPtr.Zero;
                }
                // Close the service handle
                if (svcHndl != IntPtr.Zero)
                {
                    CloseServiceHandle(svcHndl);
                    svcHndl = IntPtr.Zero;
                }
                // Free the memory
                if (tmpBuf != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(tmpBuf);
                    tmpBuf = IntPtr.Zero;
                }
            }
        }
 public static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_INFO_LEVEL dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS_FLAG lpInfo);