forked from NewLifeX/X
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWindowsService.cs
371 lines (314 loc) · 16.3 KB
/
WindowsService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using NewLife.Log;
using static NewLife.Agent.Advapi32;
namespace NewLife.Agent
{
/// <summary>Windows服务</summary>
public class WindowsService : Host
{
private IHostedService _service;
private SERVICE_STATUS _status;
private Int32 _acceptedCommands;
/// <summary>开始执行服务</summary>
/// <param name="service"></param>
public override void Run(IHostedService service)
{
if (service == null) throw new ArgumentNullException(nameof(service));
_service = service;
var num = Marshal.SizeOf(typeof(SERVICE_TABLE_ENTRY));
var intPtr = Marshal.AllocHGlobal(checked((1 + 1) * num));
try
{
// Win32OwnProcess/StartPending
_status.serviceType = 16;
_status.currentState = ServiceControllerStatus.StartPending;
_status.controlsAccepted = 0;
_status.win32ExitCode = 0;
_status.serviceSpecificExitCode = 0;
_status.checkPoint = 0;
_status.waitHint = 0;
// CanStop | CanShutdown | CanPauseAndContinue | CanHandlePowerEvent | CanHandleSessionChangeEvent
//_acceptedCommands = 1 | 4 | 2 | 64 | 128;
// CanStop | CanShutdown
_acceptedCommands = 1 | 4;
SERVICE_TABLE_ENTRY result = default;
result.callback = ServiceMainCallback;
result.name = Marshal.StringToHGlobalUni(service.ServiceName);
Marshal.StructureToPtr(result, intPtr, false);
SERVICE_TABLE_ENTRY result2 = default;
result2.callback = null;
result2.name = (IntPtr)0;
Marshal.StructureToPtr(result2, intPtr + num, false);
/*
* 如果StartServiceCtrlDispatcher函数执行成功,调用线程(也就是服务进程的主线程)不会返回,直到所有的服务进入到SERVICE_STOPPED状态。
* 调用线程扮演着控制分发的角色,干这样的事情:
* 1、在新的服务启动时启动新线程去调用服务主函数(主意:服务的任务是在新线程中做的);
* 2、当服务有请求时(注意:请求是由SCM发给它的),调用它对应的处理函数(主意:这相当于主线程“陷入”了,它在等待控制消息并对消息做处理)。
*/
XTrace.WriteLine("启动服务 {0}", service.ServiceName);
var flag = StartServiceCtrlDispatcher(intPtr);
if (!flag) XTrace.WriteLine("服务启动失败!");
}
finally
{
Marshal.FreeHGlobal(intPtr);
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
private unsafe void ServiceMainCallback(Int32 argCount, IntPtr argPointer)
{
XTrace.WriteLine("ServiceMainCallback");
fixed (SERVICE_STATUS* status = &_status)
{
// 我们直接忽略传入参数 argCount/argPointer
_statusHandle = RegisterServiceCtrlHandlerEx(_service.ServiceName, ServiceCommandCallbackEx, IntPtr.Zero);
_status.controlsAccepted = _acceptedCommands;
if ((_status.controlsAccepted & 1) != 0)
{
_status.controlsAccepted |= 4;
}
_status.currentState = ServiceControllerStatus.StartPending;
if (SetServiceStatus(_statusHandle, status))
{
// 使用线程池启动服务Start函数,并等待信号量
_startCompletedSignal = new ManualResetEvent(initialState: false);
ThreadPool.QueueUserWorkItem(ServiceQueuedMainCallback, null);
_startCompletedSignal.WaitOne();
// 设置服务状态
if (!SetServiceStatus(_statusHandle, status))
{
XTrace.WriteLine("启动服务{0}失败,{1}", _service.ServiceName, new Win32Exception().Message);
_status.currentState = ServiceControllerStatus.Stopped;
SetServiceStatus(_statusHandle, status);
}
}
}
}
private ManualResetEvent _startCompletedSignal;
private void ServiceQueuedMainCallback(Object state)
{
//var args = (String[])state;
try
{
//OnStart(args);
var source = new CancellationTokenSource();
_service.StartAsync(source.Token);
//WriteLogEntry(SR.StartSuccessful);
_status.checkPoint = 0;
_status.waitHint = 0;
_status.currentState = ServiceControllerStatus.Running;
}
catch (Exception ex)
{
XTrace.WriteException(ex);
_status.currentState = ServiceControllerStatus.Stopped;
}
_startCompletedSignal.Set();
}
private IntPtr _statusHandle;
private unsafe Int32 ServiceCommandCallbackEx(Int32 command, Int32 eventType, IntPtr eventData, IntPtr eventContext)
{
XTrace.WriteLine("ServiceCommandCallbackEx(command={0}, eventType={1}, eventData={2}, eventContext={3}", command, eventType, eventData, eventContext);
// Power | SessionChange
if (command == ControlOptions.CONTROL_POWEREVENT || command == ControlOptions.CONTROL_SESSIONCHANGE) return 0;
fixed (SERVICE_STATUS* status = &_status)
{
if (command == ControlOptions.CONTROL_INTERROGATE)
{
SetServiceStatus(_statusHandle, status);
}
else if (_status.currentState != ServiceControllerStatus.ContinuePending &&
_status.currentState != ServiceControllerStatus.StartPending &&
_status.currentState != ServiceControllerStatus.StopPending &&
_status.currentState != ServiceControllerStatus.PausePending)
{
switch (command)
{
//case ControlOptions.CONTROL_CONTINUE:
// if (_status.currentState == ServiceControllerStatus.Paused)
// {
// _status.currentState = ServiceControllerStatus.ContinuePending;
// SetServiceStatus(_statusHandle, status);
// //ThreadPool.QueueUserWorkItem(delegate
// //{
// // DeferredContinue();
// //});
// }
// break;
//case ControlOptions.CONTROL_PAUSE:
// if (_status.currentState == ServiceControllerStatus.Running)
// {
// _status.currentState = ServiceControllerStatus.PausePending;
// SetServiceStatus(_statusHandle, status);
// //ThreadPool.QueueUserWorkItem(delegate
// //{
// // DeferredPause();
// //});
// }
// break;
case ControlOptions.CONTROL_STOP:
var currentState = _status.currentState;
if (_status.currentState == ServiceControllerStatus.Paused ||
_status.currentState == ServiceControllerStatus.Running)
{
// 设置为StopPending,然后线程池去执行停止
_status.currentState = ServiceControllerStatus.StopPending;
SetServiceStatus(_statusHandle, status);
_status.currentState = currentState;
ThreadPool.QueueUserWorkItem(s => DeferredStop());
}
break;
case ControlOptions.CONTROL_SHUTDOWN:
if (_status.currentState == ServiceControllerStatus.Paused ||
_status.currentState == ServiceControllerStatus.Running)
{
_status.checkPoint = 0;
_status.waitHint = 0;
_status.currentState = ServiceControllerStatus.Stopped;
SetServiceStatus(_statusHandle, status);
ThreadPool.QueueUserWorkItem(s => DeferredStop());
}
break;
default:
//ThreadPool.QueueUserWorkItem(delegate
//{
// DeferredCustomCommand(command);
//});
break;
}
}
}
return 0;
}
private unsafe void DeferredStop()
{
fixed (SERVICE_STATUS* status = &_status)
{
var currentState = _status.currentState;
_status.checkPoint = 0;
_status.waitHint = 0;
_status.currentState = ServiceControllerStatus.StopPending;
SetServiceStatus(_statusHandle, status);
try
{
var source = new CancellationTokenSource();
_service.StopAsync(source.Token);
_status.currentState = ServiceControllerStatus.Stopped;
SetServiceStatus(_statusHandle, status);
}
catch (Exception ex)
{
XTrace.WriteException(ex);
_status.currentState = currentState;
SetServiceStatus(_statusHandle, status);
}
}
}
#region 服务状态和控制
/// <summary>服务是否已安装</summary>
/// <param name="serviceName">服务名</param>
/// <returns></returns>
public override Boolean IsInstalled(String serviceName)
{
using var manager = new SafeServiceHandle(OpenSCManager(null, null, ServiceControllerOptions.SC_MANAGER_CONNECT));
if (manager == null || manager.IsInvalid) return false;
using var service = new SafeServiceHandle(OpenService(manager, serviceName, ServiceOptions.SERVICE_QUERY_CONFIG));
if (service == null || service.IsInvalid) return false;
return true;
}
/// <summary>服务是否已启动</summary>
/// <param name="serviceName">服务名</param>
/// <returns></returns>
public override unsafe Boolean IsRunning(String serviceName)
{
using var manager = new SafeServiceHandle(OpenSCManager(null, null, ServiceControllerOptions.SC_MANAGER_CONNECT));
if (manager == null || manager.IsInvalid) return false;
using var service = new SafeServiceHandle(OpenService(manager, serviceName, ServiceOptions.SERVICE_QUERY_STATUS));
if (service == null || service.IsInvalid) return false;
SERVICE_STATUS status = default;
if (!QueryServiceStatus(service, &status))
throw new Win32Exception(Marshal.GetLastWin32Error());
return status.currentState == ServiceControllerStatus.Running;
}
/// <summary>安装服务</summary>
/// <param name="serviceName">服务名</param>
/// <param name="displayName"></param>
/// <param name="binPath"></param>
/// <param name="description"></param>
/// <returns></returns>
public override Boolean Install(String serviceName, String displayName, String binPath, String description)
{
XTrace.WriteLine("{0}.Install {1}, {2}, {3}, {4}", GetType().Name, serviceName, displayName, binPath, description);
using var manager = new SafeServiceHandle(OpenSCManager(null, null, ServiceControllerOptions.SC_MANAGER_CREATE_SERVICE));
if (manager.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
using var service = new SafeServiceHandle(CreateService(manager, serviceName, displayName, ServiceOptions.SERVICE_ALL_ACCESS, 0x10, 2, 1, binPath, null, 0, null, null, null));
if (service.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
// 设置描述信息
if (!description.IsNullOrEmpty())
{
SERVICE_DESCRIPTION sd;
sd.Description = description;
var lpInfo = Marshal.AllocHGlobal(Marshal.SizeOf(sd));
try
{
Marshal.StructureToPtr(sd, lpInfo, false);
const Int32 SERVICE_CONFIG_DESCRIPTION = 1;
ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, lpInfo);
}
finally
{
Marshal.FreeHGlobal(lpInfo);
}
}
return true;
}
/// <summary>卸载服务</summary>
/// <param name="serviceName">服务名</param>
/// <returns></returns>
public override unsafe Boolean Remove(String serviceName)
{
XTrace.WriteLine("{0}.Remove {1}", GetType().Name, serviceName);
using var manager = new SafeServiceHandle(OpenSCManager(null, null, ServiceControllerOptions.SC_MANAGER_ALL));
if (manager.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
using var service = new SafeServiceHandle(OpenService(manager, serviceName, ServiceOptions.SERVICE_STOP | ServiceOptions.STANDARD_RIGHTS_DELETE));
if (service.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
SERVICE_STATUS status = default;
ControlService(service, ControlOptions.CONTROL_STOP, &status);
if (DeleteService(service) == 0) throw new Win32Exception(Marshal.GetLastWin32Error());
return true;
}
/// <summary>启动服务</summary>
/// <param name="serviceName">服务名</param>
/// <returns></returns>
public override Boolean Start(String serviceName)
{
XTrace.WriteLine("{0}.Start {1}", GetType().Name, serviceName);
using var manager = new SafeServiceHandle(OpenSCManager(null, null, ServiceControllerOptions.SC_MANAGER_CONNECT));
if (manager.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
using var service = new SafeServiceHandle(OpenService(manager, serviceName, ServiceOptions.SERVICE_START));
if (service.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
if (!StartService(service, 0, IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
return true;
}
/// <summary>停止服务</summary>
/// <param name="serviceName">服务名</param>
/// <returns></returns>
public override unsafe Boolean Stop(String serviceName)
{
XTrace.WriteLine("{0}.Stop {1}", GetType().Name, serviceName);
using var manager = new SafeServiceHandle(OpenSCManager(null, null, ServiceControllerOptions.SC_MANAGER_ALL));
if (manager.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
using var service = new SafeServiceHandle(OpenService(manager, serviceName, ServiceOptions.SERVICE_STOP));
if (service.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
SERVICE_STATUS status = default;
if (!ControlService(service, ControlOptions.CONTROL_STOP, &status))
throw new Win32Exception(Marshal.GetLastWin32Error());
return true;
}
#endregion
}
}