/Visual Studio 2008/CSWindowsServiceRecoveryProperty/ServiceRecoveryProperty.cs

# · C# · 219 lines · 125 code · 23 blank · 71 comment · 21 complexity · e981228f95779076a31c1964f9416590 MD5 · raw file

  1. /********************************** Module Header **********************************\
  2. * Module Name: ServiceRecoveryProperties.cs
  3. * Project: CSWindowsServiceRecoveryProperty
  4. * Copyright (c) Microsoft Corporation.
  5. *
  6. * This file demonstrates how to configure service recovery property include grant
  7. * shutdown privilege to the process, so that we can configure a special option in
  8. * "Recovery" tab - "Restart Computer Options...".
  9. *
  10. * This source is subject to the Microsoft Public License.
  11. * See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
  12. * All other rights reserved.
  13. *
  14. * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
  15. * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
  16. * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
  17. \***********************************************************************************/
  18. #region Using directives
  19. using System;
  20. using System.ComponentModel;
  21. using System.Runtime.InteropServices;
  22. using System.Diagnostics;
  23. using System.Collections.Generic;
  24. #endregion
  25. namespace CSWindowsServiceRecoveryProperty
  26. {
  27. internal class ServiceRecoveryProperty
  28. {
  29. /// <summary>
  30. /// Change the recovery property of a Windows service.
  31. /// </summary>
  32. /// <param name="scName">The name of the Windows service</param>
  33. /// <param name="scActions">
  34. /// A list of SC_ACTION representing the actions that the service control
  35. /// manager can perform.
  36. /// </param>
  37. /// <param name="resetPeriod">
  38. /// The time after which to reset the failure count to zero if there are no
  39. /// failures, in seconds.
  40. /// </param>
  41. /// <param name="command">
  42. /// The command line of the process for the CreateProcess function to execute
  43. /// in response to the SC_ACTION_RUN_COMMAND service controller action. This
  44. /// process runs under the same account as the service.
  45. /// </param>
  46. /// <param name="fFailureActionsOnNonCrashFailures">
  47. /// If this member is true and the service has configured failure actions,
  48. /// the failure actions are queued if the service process terminates without
  49. /// reporting a status of SERVICE_STOPPED or if it enters the SERVICE_STOPPED
  50. /// state but the dwWin32ExitCode member of the SERVICE_STATUS structure is
  51. /// not ERROR_SUCCESS (0). If this member is false and the service has
  52. /// configured failure actions, the failure actions are queued only if the
  53. /// service terminates without reporting a status of SERVICE_STOPPED.
  54. /// </param>
  55. /// <param name="rebootMsg">
  56. /// The message to be broadcast to server users before rebooting in response
  57. /// to the SC_ACTION_REBOOT service controller action.
  58. /// </param>
  59. public static void ChangeRecoveryProperty(string scName,
  60. List<SC_ACTION> scActions, int resetPeriod, string command,
  61. bool fFailureActionsOnNonCrashFailures, string rebootMsg)
  62. {
  63. SafeServiceHandle hSCManager = null;
  64. SafeServiceHandle hService = null;
  65. IntPtr hGlobal = IntPtr.Zero;
  66. try
  67. {
  68. // Open the service control manager.
  69. hSCManager = Win32.OpenSCManager(null, null, Win32.SERVICE_QUERY_CONFIG);
  70. if (hSCManager.IsInvalid)
  71. {
  72. throw new Win32Exception();
  73. }
  74. // Open the service.
  75. hService = Win32.OpenService(hSCManager, scName, Win32.SERVICE_ALL_ACCESS);
  76. if (hService.IsInvalid)
  77. {
  78. throw new Win32Exception();
  79. }
  80. int numActions = scActions.Count;
  81. int[] falureActions = new int[numActions * 2];
  82. bool needShutdownPrivilege = false;
  83. int i = 0;
  84. // We need to copy the actions in scFailureActionArray to an
  85. // unmanaged memory through Marshal.Copy.
  86. foreach (SC_ACTION scAction in scActions)
  87. {
  88. falureActions[i] = scAction.Type;
  89. falureActions[++i] = scAction.Delay;
  90. i++;
  91. if (scAction.Type == (int)SC_ACTION_TYPE.RebootComputer)
  92. {
  93. needShutdownPrivilege = true;
  94. }
  95. }
  96. // If we need shutdown privilege, then grant it to this process.
  97. if (needShutdownPrivilege)
  98. {
  99. GrantShutdownPrivilege();
  100. }
  101. // Allocate memory.
  102. hGlobal = Marshal.AllocHGlobal(falureActions.Length * Marshal.SizeOf(typeof(int)));
  103. // Copies data from a one-dimensional, managed 32-bit signed integer
  104. // array to an unmanaged memory pointer.
  105. Marshal.Copy(falureActions, 0, hGlobal, falureActions.Length);
  106. // Set the SERVICE_FAILURE_ACTIONS struct.
  107. SERVICE_FAILURE_ACTIONS scFailureActions = new SERVICE_FAILURE_ACTIONS();
  108. scFailureActions.cActions = numActions;
  109. scFailureActions.dwResetPeriod = resetPeriod;
  110. scFailureActions.lpCommand = command;
  111. scFailureActions.lpRebootMsg = rebootMsg;
  112. scFailureActions.lpsaActions = hGlobal;
  113. // Call the ChangeServiceFailureActions function abstraction of the
  114. // ChangeServiceConfig2 function.
  115. if (!Win32.ChangeServiceFailureActions(hService,
  116. Win32.SERVICE_CONFIG_FAILURE_ACTIONS, ref scFailureActions))
  117. {
  118. throw new Win32Exception();
  119. }
  120. // Restart Computer Options....
  121. SERVICE_FAILURE_ACTIONS_FLAG flag = new SERVICE_FAILURE_ACTIONS_FLAG();
  122. flag.fFailureActionsOnNonCrashFailures = fFailureActionsOnNonCrashFailures;
  123. // Call the FailureActionsOnNonCrashFailures function, the
  124. // abstraction of the ChangeServiceConfig2 function.
  125. if (!Win32.FailureActionsOnNonCrashFailures(hService,
  126. Win32.SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, ref flag))
  127. {
  128. throw new Win32Exception();
  129. }
  130. }
  131. finally
  132. {
  133. // Close the service control manager handle.
  134. if (hSCManager != null && !hSCManager.IsInvalid)
  135. {
  136. hSCManager.Dispose();
  137. hSCManager = null;
  138. }
  139. // Close the service handle.
  140. if (hService != null && !hService.IsInvalid)
  141. {
  142. hService.Dispose();
  143. hService = null;
  144. }
  145. // Free the unmanaged memory.
  146. if (hGlobal != IntPtr.Zero)
  147. {
  148. Marshal.FreeHGlobal(hGlobal);
  149. hGlobal = IntPtr.Zero;
  150. }
  151. }
  152. }
  153. /// <summary>
  154. /// Grant shutdown privilege to the process.
  155. /// </summary>
  156. private static void GrantShutdownPrivilege()
  157. {
  158. SafeTokenHandle hToken = null;
  159. try
  160. {
  161. // Open the access token associated with the current process.
  162. if (!Win32.OpenProcessToken(Process.GetCurrentProcess().Handle,
  163. Win32.TOKEN_ADJUST_PRIVILEGES | Win32.TOKEN_QUERY, out hToken))
  164. {
  165. throw new Win32Exception();
  166. }
  167. // Retrieve the locally unique identifier (LUID) used on a specified
  168. // system to locally represent the specified privilege name.
  169. long Luid = 0;
  170. if (!Win32.LookupPrivilegeValue(null, Win32.SE_SHUTDOWN_NAME, ref Luid))
  171. {
  172. throw new Win32Exception();
  173. }
  174. TOKEN_PRIVILEGES tokenPrivileges = new TOKEN_PRIVILEGES();
  175. tokenPrivileges.PrivilegeCount = 1;
  176. tokenPrivileges.Privileges.Luid = Luid;
  177. tokenPrivileges.Privileges.Attributes = Win32.SE_PRIVILEGE_ENABLED;
  178. // Enable privileges in the specified access token.
  179. int retLen = 0;
  180. if (!Win32.AdjustTokenPrivileges(hToken, false, ref tokenPrivileges,
  181. 0, IntPtr.Zero, ref retLen))
  182. {
  183. throw new Win32Exception();
  184. }
  185. }
  186. finally
  187. {
  188. if (hToken != null && !hToken.IsInvalid)
  189. {
  190. hToken.Dispose();
  191. hToken = null;
  192. }
  193. }
  194. }
  195. }
  196. }