1 class 设置服务恢复选项
2 {
3 public static void Main(string[] args)
4 {
6 ServiceRecovery.UpdateServiceConfig("DEMO");
7 Console.WriteLine("ok");
8 }
9 }
10
11 class ServiceRecovery
12 {
13 #region NativeMethod
14
15 [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
16 [return: MarshalAs(UnmanagedType.Bool)]
17 public static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, IntPtr lpInfo);
18
19 [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
20 public static extern IntPtr OpenSCManager(string MachineName, string DatabaseName, uint DesiredAcces);
21
22 [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
23 public static extern IntPtr OpenService(IntPtr scHandle, string serviceName, int DesiredAccess);
24
25 [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
26 public static extern bool CloseServiceHandle(IntPtr hSCObject);
27
28 [DllImport("advapi32.dll")]
29 public static extern IntPtr LockServiceDatabase(IntPtr hSCManager);
30
31 [DllImport("advapi32.dll")]
32 public static extern bool UnlockServiceDatabase(IntPtr hSCManager);
33
34 [DllImport("kernel32.dll")]
35 public static extern int GetLastError();
36
37 [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]
38 public static extern bool ChangeServiceFailureActions(IntPtr hService, int dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS lpInfo);
39
40 [DllImport("advapi32.dll")]
41 public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, [MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, ref int ReturnLength);
42
43 [DllImport("advapi32.dll")]
44 public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref long lpLuid);
45
46 [DllImport("advapi32.dll")]
47 public static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
48
49 [DllImport("kernel32.dll")]
50 public static extern IntPtr GetCurrentProcess();
51
52 [DllImport("kernel32.dll")]
53 public static extern bool CloseHandle(IntPtr hndl);
54
55 #endregion
56
57 private const int TOKEN_ADJUST_PRIVILEGES = 32;
58 private const int TOKEN_QUERY = 8;
59 private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
60 private const int SE_PRIVILEGE_ENABLED = 2;
61
62 [StructLayout(LayoutKind.Sequential, Pack = 1)]
63 public struct TOKEN_PRIVILEGES
64 {
65 public int PrivilegeCount;
66 public LUID_AND_ATTRIBUTES Privileges;
67 }
68 [StructLayout(LayoutKind.Sequential)]
69 public struct LUID_AND_ATTRIBUTES
70 {
71 public long Luid;
72 public int Attributes;
73 }
74
75 [StructLayout(LayoutKind.Sequential)]
76 public struct SERVICE_FAILURE_ACTIONS
77 {
78 /// <summary>
79 /// 在此时间之后重置失败计数
80 /// </summary>
81 public int dwResetPeriod;
82 public string lpRebootMsg;
83 public string lpCommand;
84 public int cActions;
85 public int lpsaActions;
86 }
87
88 private const int SC_MANAGER_ALL_ACCESS = 0xF003F;
89 private const int SERVICE_ALL_ACCESS = 0xF01FF;
90 private const int SERVICE_CONFIG_DESCRIPTION = 0x1;
91 private const int SERVICE_CONFIG_FAILURE_ACTIONS = 0x2;
92 private const int ERROR_ACCESS_DENIED = 5;
93
94 static string logMsgBase = "";
95 static string ServiceName = "";
96
97 public static void UpdateServiceConfig(string serviceName)
98 {
99 logMsgBase = "Windows 服务" + ServiceName + ":";
100 ServiceName = serviceName;
101 var FailureActions = new ArrayList();
102
103 //第一次失败
104 FailureActions.Add(new FailureAction(RecoverAction.Restart, 0));
105 //第二次失败
106 FailureActions.Add(new FailureAction(RecoverAction.None, 0));
107 //后续失败
108 FailureActions.Add(new FailureAction(RecoverAction.Reboot, 0));
109
110 int numActions = FailureActions.Count;
111
112 IntPtr scmHndl = IntPtr.Zero;
113 IntPtr svcHndl = IntPtr.Zero;
114 IntPtr tmpBuf = IntPtr.Zero;
115 IntPtr svcLock = IntPtr.Zero;
116
117 bool rslt = false;
118 try
119 {
120 scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
121 if (scmHndl.ToInt32() <= 0)
122 {
123 LogInstallMessage(EventLogEntryType.Error, logMsgBase + "打开服务控制管理器失败");
124 return;
125 }
126 svcLock = LockServiceDatabase(scmHndl);
127 if (svcLock.ToInt32() <= 0)
128 {
129
130 LogInstallMessage(EventLogEntryType.Error, logMsgBase + "无法锁定服务数据库进行写入");
131 return;
132 }
133 svcHndl = OpenService(scmHndl, ServiceName, SERVICE_ALL_ACCESS);
134
135 if (svcHndl.ToInt32() <= 0)
136 {
137 LogInstallMessage(EventLogEntryType.Information, logMsgBase + "无法打开服务");
138 return;
139 }
140
141 int[] actions = new int[numActions * 2];
142 int currInd = 0;
143 bool needShutdownPrivilege = false;
144 foreach (FailureAction fa in FailureActions)
145 {
146 actions[currInd] = (int)fa.Type;
147 actions[++currInd] = fa.Delay;
148 currInd++;
149 if (fa.Type == RecoverAction.Reboot)
150 {
151 needShutdownPrivilege = true;
152 }
153 }
154
155 if (needShutdownPrivilege)
156 {
157 rslt = GrandShutdownPrivilege();
158 if (!rslt) return;
159 }
160
161 tmpBuf = Marshal.AllocHGlobal(numActions * 8);
162 Marshal.Copy(actions, 0, tmpBuf, numActions * 2);
163 SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS();
164
165 sfa.cActions = numActions;
166 sfa.dwResetPeriod = 0;
167 sfa.lpCommand = "";
168 sfa.lpRebootMsg = "";
169 sfa.lpsaActions = tmpBuf.ToInt32();
170
171 rslt = ChangeServiceFailureActions(svcHndl, SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa);
172 if (!rslt)
173 {
174 int err = GetLastError();
175 if (err == ERROR_ACCESS_DENIED)
176 {
177 throw new Exception(logMsgBase + "配置失败时恢复的操作失败");
178 }
179 }
180 Marshal.FreeHGlobal(tmpBuf); tmpBuf = IntPtr.Zero;
181 LogInstallMessage(EventLogEntryType.Information, logMsgBase + "已成功配置失败时恢复的操作");
182
183 }
184 catch (Exception ex)
185 {
186 LogInstallMessage(EventLogEntryType.Error, ex.Message);
187 }
188 finally
189 {
190 if (scmHndl != IntPtr.Zero)
191 {
192 if (svcLock != IntPtr.Zero)
193 {
194 UnlockServiceDatabase(svcLock);
195 svcLock = IntPtr.Zero;
196 }
197 CloseServiceHandle(scmHndl);
198 scmHndl = IntPtr.Zero;
199 }
200 if (svcHndl != IntPtr.Zero)
201 {
202 CloseServiceHandle(svcHndl);
203 svcHndl = IntPtr.Zero;
204 }
205 if (tmpBuf != IntPtr.Zero)
206 {
207 Marshal.FreeHGlobal(tmpBuf);
208 tmpBuf = IntPtr.Zero;
209 }
210 }
211
212 }
213
214 static bool GrandShutdownPrivilege()
215 {
216 bool retRslt = false;
217 IntPtr hToken = IntPtr.Zero;
218 IntPtr myProc = IntPtr.Zero;
219 TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES();
220 long Luid = 0;
221 int retLen = 0;
222
223 try
224 {
225
226 myProc = GetCurrentProcess();
227 bool rslt = OpenProcessToken(myProc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref hToken);
228 if (!rslt) return retRslt;
229
230 LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref Luid);
231
232 tkp.PrivilegeCount = 1;
233 tkp.Privileges.Luid = Luid;
234 tkp.Privileges.Attributes = SE_PRIVILEGE_ENABLED;
235
236 rslt = AdjustTokenPrivileges(hToken, false, ref tkp, 0, IntPtr.Zero, ref retLen);
237
238 if (GetLastError() != 0)
239 {
240 throw new Exception("无法授予关机特权");
241 }
242 retRslt = true;
243 }
244 catch (Exception ex)
245 {
246 LogInstallMessage(EventLogEntryType.Error, logMsgBase + ex.Message);
247 }
248 finally
249 {
250 if (hToken != IntPtr.Zero)
251 {
252 CloseHandle(hToken);
253 }
254 }
255 return retRslt;
256 }
257
258 static void LogInstallMessage(EventLogEntryType logLevel, string msg)
259 {
260 try
261 {
262 EventLog.WriteEntry(ServiceName, msg, logLevel);
263 }
264 catch
265 {
266
267 }
268 }
269
270 enum RecoverAction
271 {
272 /// <summary>
273 /// 无操作
274 /// </summary>
275 None = 0,
276 /// <summary>
277 /// 重新启动服务
278 /// </summary>
279 Restart = 1,
280 /// <summary>
281 /// 重启计算机
282 /// </summary>
283 Reboot = 2,
284 /// <summary>
285 /// 运行一个程序
286 /// </summary>
287 RunCommand = 3
288 }
289
290 class FailureAction
291 {
292 private RecoverAction type = RecoverAction.None;
293 /// <summary>
294 /// 在此时间之后重新启动服务
295 /// </summary>
296 private int delay = 0;
297 public FailureAction()
298 {
299
300 }
301 public FailureAction(RecoverAction actionType, int actionDelay)
302 {
303 this.type = actionType;
304 this.delay = actionDelay;
305 }
306
307 public RecoverAction Type
308 {
309 get { return type; }
310 set { type = value; }
311 }
312 /// <summary>
313 /// 在此时间之后重新启动服务
314 /// </summary>
315 public int Delay
316 {
317 get { return delay; }
318 set { delay = value; }
319 }
320
321 }
322 }