研究下以前抄的C++版Windows服务

前言

以前做项目需要用到Windows服务,于是随便在网上找了一篇C++版的Windows服务代码,封装的很好,直接拿来用就可以。现在项目需求发生了一点小变化,需要服务时刻监听启动的exe是否异常,如果异常就重新启动。于是研究下代码,顺便熟悉下Windows服务。

文件构成

整个文件有两部分构成

  1. CServiceBase类
  2. ServiceInstall文件,包含安装、卸载、更新服务描述

程序入口处理

因为我们最终需要打包自动安装,卸载程序的时候自动卸载服务,所以在main函数中可以通过参数判断,程序是安装服务,还是卸载服务或者是运行服务。

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
// 服务启动类型
#define SERVICE_START_TYPE SERVICE_AUTO_START


// 服务依赖项
#define SERVICE_DEPENDENCIES L""


// 服务配置为以 LocalSystem(而非 LocalService)身份运行
#define SERVICE_ACCOUNT NULL //L"NT AUTHORITY\\LocalService"


// The password to the service account name
#define SERVICE_PASSWORD NULL

int _tmain(int argc, TCHAR* argv[])
{

if ((argc > 1) && ((*argv[1] == L'-' || (*argv[1] == L'/'))))
{
if (_wcsicmp(L"install", argv[1] + 1) == 0)
{
//通过命令行的方式注册服务
InstallService(
SERVICE_NAME, // 服务的名称,不用多说,一般为sc使用的名称或net使用的名称
SERVICE_DISPLAY_NAME, // 服务显示的名称,显示给用户的名称
/*
#define SERVICE_BOOT_START 0x00000000 //系统加载程序加载驱动启动,仅对驱动程序有效
#define SERVICE_SYSTEM_START 0x00000001 //由 IOInitSystem 函数启动的设备驱动程序
#define SERVICE_AUTO_START 0x00000002 //操作系统启动的时候启动
#define SERVICE_DEMAND_START 0x00000003 //手动启动
#define SERVICE_DISABLED 0x00000004 //服务已被禁用

// 服务启动类型,分为 以上五种,一般为SERVICE_AUTO_START,
// 0和1针对于驱动程序,一般操作系统启动不起来大部分原因是驱动程序加载不上
*/
SERVICE_START_TYPE,
SERVICE_DEPENDENCIES, // 依赖的服务,一般没有依赖,当然如果你依赖另一个服务要填它的SERVICE_NAME
// 服务运行的帐号,有四个选项
// 1. 当前登录账号,一般我们是不知道用户电脑的账号的,而且也不可能统一账号和密码
// 2. LocalService,计算机上非特权用户
// 3. LocalSystem,服务控制管理员使用的帐户
// 4. NetworkService,提供广泛的本地特权的帐户,该帐户将计算机的凭据提供给所有远程服务器
// 如果此参数为NULL,则 CreateService使用 LocalSystem帐户
// NT AUTHORITY\LocalService 为LocalService账号
// NT AUTHORITY\NetworkService 为NetworkService账号
// 具体请看参考文献2、3、4和5
SERVICE_ACCOUNT,
SERVICE_PASSWORD // 帐号的密码,一般填NULL
);
//更新服务描述
updateServiceDescription(SERVICE_NAME,SERVICE_DESC);
}
else if (_wcsicmp(L"remove", argv[1] + 1) == 0)
{
//通过命令行的方式卸载服务
// "-remove" or "/remove".
UninstallService(SERVICE_NAME);
}
}
else
{
CImpCertService service(SERVICE_NAME);
if (!CServiceBase::Run(service))
{
WriteToLog("服务启动失败,错误码:"+GetLastError());
}
}
return 0;
}

这样打包的时候就可以使用命令参数来进行安装或卸载。

比如编译出来是 YYService.exe,我用的是inno setup打包,脚本语言就可以写成

1
2
3
4
5
6
7
[Run]
Filename:"{app}/YYService.exe"; Parameters:"-install";Flags:runhidden;StatusMsg:"正在安装服务"
Filename: "net.exe"; Parameters: "start YYService"; Flags:runhidden;StatusMsg:"正在启动服务"

[UninstallRun]
Filename:"net.exe"; Parameters:"stop YYService";Flags:runhidden waituntilterminated;
Filename:"{app}/YYService.exe"; Parameters:"-remove";Flags:runhidden waituntilterminated;

服务启动与停止

我们首先看服务怎么运行的。

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
#pragma once

#include <windows.h>


class CServiceBase
{
public:

// 向SCM注册服务的可执行程序文件,调用Run方法后,SCM后发出Start命令,这将调用服务中的OnStart方法
static BOOL Run(CServiceBase &service);

// 构造函数,允许你指定服务可以停止、暂停和继续
CServiceBase(PWSTR pszServiceName,
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE);

// 服务析构函数
virtual ~CServiceBase(void);

// 停止服务
void Stop();

protected:

// 在派生类中实现,当调用Run时,会使SCM调用OnStart
virtual void OnStart(DWORD dwArgc, PWSTR *pszArgv);

// 在派生类中实现,当服务停止时SCM会调用该方法
virtual void OnStop();

// 在派生类中实现,当服务暂停时,SCM会调用该方法
virtual void OnPause();

// 在派生类中实现,当服务继续时,SCM会调用该方法
virtual void OnContinue();

// 当系统关机时,会调用该方法
virtual void OnShutdown();

// 设置服务状态,并将状态报告给SCM
void SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode = NO_ERROR,
DWORD dwWaitHint = 0);

// 将日志信息写入Windows日志中
void WriteEventLogEntry(PWSTR pszMessage, WORD wType);

// 将错误信息写入到Windows日志中
void WriteErrorLogEntry(PWSTR pszFunction,
DWORD dwError = GetLastError());

private:

// 服务的入口点. 注册并启动服务
static void WINAPI ServiceMain(DWORD dwArgc, LPWSTR *lpszArgv);

// 这个函数将会在SCM发送控制码给这个服务的时候调用
static void WINAPI ServiceCtrlHandler(DWORD dwCtrl);

// 启动服务
void Start(DWORD dwArgc, PWSTR *pszArgv);

// 暂停服务
void Pause();

// 继续服务
void Continue();

// 当系统关机时执行
void Shutdown();

// 子类对象
static CServiceBase *s_service;

// 服务名称
PWSTR m_name;

// 服务状态
SERVICE_STATUS m_status;

// 服务状态句柄
SERVICE_STATUS_HANDLE m_statusHandle;
};
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
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447

#pragma region Includes
#include "ServiceBase.h"
#include <assert.h>
#include <strsafe.h>
#pragma endregion


#pragma region Static Members

// 初始化
CServiceBase *CServiceBase::s_service = NULL;

// 目的:向SCM注册服务的可执行文件,在调用这个方法之后,SCM会发出Start命令从而调用服务的OnStart方法
BOOL CServiceBase::Run(CServiceBase &service)
{
s_service = &service;

SERVICE_TABLE_ENTRY serviceTable[] =
{
{ service.m_name, ServiceMain },
{ NULL, NULL }
};

// 将服务的主线程连接到SCM。
// StartServiceCtrlDispatcher时,程序由服务控制管理器接管
// SCM 根据需要启动的服务名称,在传入的数组指针中,找到对应的入口函数,然后调用它
// 当对应的入口函数返回时结束服务,并将后续代码的控制权转交给对应主进程,由主进程接着执行后面的代码
// 注意:此处有时间要求,如果时间超过30秒,就应该另起子线程进行初始化
return StartServiceCtrlDispatcher(serviceTable);
}


// 服务启动的入口点,如果学过Windows驱动就很容易理解这个概念
// 注册服务函数,当SCM发送控制码时,会调用注册的对应函数
void WINAPI CServiceBase::ServiceMain(DWORD dwArgc, PWSTR *pszArgv)
{
assert(s_service != NULL);

// 注册服务函数
s_service->m_statusHandle = RegisterServiceCtrlHandler(
s_service->m_name, ServiceCtrlHandler);
if (s_service->m_statusHandle == NULL)
{
throw GetLastError();
}

// Start the service.
s_service->Start(dwArgc, pszArgv);
}



// PURPOSE: 注册控制码对应的函数
//
// PARAMETERS:
// * dwCtrlCode - 控制码
//
// SERVICE_CONTROL_CONTINUE
// SERVICE_CONTROL_INTERROGATE
// SERVICE_CONTROL_NETBINDADD
// SERVICE_CONTROL_NETBINDDISABLE
// SERVICE_CONTROL_NETBINDREMOVE
// SERVICE_CONTROL_PARAMCHANGE
// SERVICE_CONTROL_PAUSE
// SERVICE_CONTROL_SHUTDOWN
// SERVICE_CONTROL_STOP
//
void WINAPI CServiceBase::ServiceCtrlHandler(DWORD dwCtrl)
{
switch (dwCtrl)
{
case SERVICE_CONTROL_STOP: s_service->Stop(); break;
case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
case SERVICE_CONTROL_INTERROGATE: break;
default: break;
}
}

#pragma endregion


#pragma region Service Constructor and Destructor

//
//
// PARAMETERS:
// * pszServiceName - 服务名称
// * fCanStop - 服务是否可用暂停
// * fCanShutdown - 当关机的时候是否会通知服务
// * fCanPauseContinue - 服务是否可以继续
//
CServiceBase::CServiceBase(PWSTR pszServiceName,
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue)
{
// Service name must be a valid string and cannot be NULL.
m_name = (pszServiceName == NULL) ? L"" : pszServiceName;

m_statusHandle = NULL;

// The service runs in its own process.
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;

// The service is starting.
m_status.dwCurrentState = SERVICE_START_PENDING;

// The accepted commands of the service.
DWORD dwControlsAccepted = 0;
if (fCanStop)
dwControlsAccepted |= SERVICE_ACCEPT_STOP;
if (fCanShutdown)
dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
if (fCanPauseContinue)
dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
m_status.dwControlsAccepted = dwControlsAccepted;

m_status.dwWin32ExitCode = NO_ERROR;
m_status.dwServiceSpecificExitCode = 0;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
}


//
// FUNCTION: CServiceBase::~CServiceBase()
//
// PURPOSE: The virtual destructor of CServiceBase.
//
CServiceBase::~CServiceBase(void)
{
}

#pragma endregion


#pragma region Service Start, Stop, Pause, Continue, and Shutdown

//
// 启动服务
//
// PURPOSE: The function starts the service. It calls the OnStart virtual
// function in which you can specify the actions to take when the service
// starts. If an error occurs during the startup, the error will be logged
// in the Application event log, and the service will be stopped.
//
// PARAMETERS:
// * dwArgc - number of command line arguments
// * lpszArgv - array of command line arguments
//
void CServiceBase::Start(DWORD dwArgc, PWSTR *pszArgv)
{
try
{
//告诉SCM启动中
SetServiceStatus(SERVICE_START_PENDING);

// 调用子类的启动函数
OnStart(dwArgc, pszArgv);

//告诉SCM启动了
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
// Log the error.
WriteErrorLogEntry(L"Service Start", dwError);

// 设置服务停止
SetServiceStatus(SERVICE_STOPPED, dwError);
}
catch (...)
{
// 写错误日志
WriteEventLogEntry(L"Service failed to start.", EVENTLOG_ERROR_TYPE);

// 设置服务停止
SetServiceStatus(SERVICE_STOPPED);
}
}



// 留着子类实现,默认不做任何事情
void CServiceBase::OnStart(DWORD dwArgc, PWSTR *pszArgv)
{
}


//
// FUNCTION: CServiceBase::Stop()
//
// PURPOSE: The function stops the service. It calls the OnStop virtual
// function in which you can specify the actions to take when the service
// stops. If an error occurs, the error will be logged in the Application
// event log, and the service will be restored to the original state.
//
void CServiceBase::Stop()
{
DWORD dwOriginalState = m_status.dwCurrentState;
try
{
// 告诉SCM 停止服务中
SetServiceStatus(SERVICE_STOP_PENDING);

// 子类停止服务做得事情
OnStop();

// 告诉SCM已经停止
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
// 写错误日志
WriteErrorLogEntry(L"Service Stop", dwError);

// 设置服务状态
SetServiceStatus(dwOriginalState);
}
catch (...)
{
// 写错误日志
WriteEventLogEntry(L"Service failed to stop.", EVENTLOG_ERROR_TYPE);

// 设置服务状态
SetServiceStatus(dwOriginalState);
}
}


//
// 默认不做任何事情
void CServiceBase::OnStop()
{
}


//
// FUNCTION: CServiceBase::Pause()
//
// PURPOSE: The function pauses the service if the service supports pause
// and continue. It calls the OnPause virtual function in which you can
// specify the actions to take when the service pauses. If an error occurs,
// the error will be logged in the Application event log, and the service
// will become running.
//
void CServiceBase::Pause()
{
try
{
// 告诉SCM暂停中
SetServiceStatus(SERVICE_PAUSE_PENDING);

// 子类停止时要做的事情
OnPause();

// 告诉SCM已停止
SetServiceStatus(SERVICE_PAUSED);
}
catch (DWORD dwError)
{
// Log the error.
WriteErrorLogEntry(L"Service Pause", dwError);

// Tell SCM that the service is still running.
SetServiceStatus(SERVICE_RUNNING);
}
catch (...)
{
// Log the error.
WriteEventLogEntry(L"Service failed to pause.", EVENTLOG_ERROR_TYPE);

// Tell SCM that the service is still running.
SetServiceStatus(SERVICE_RUNNING);
}
}


//
void CServiceBase::OnPause()
{
}


// 告诉SCM继续服务
void CServiceBase::Continue()
{
try
{
// Tell SCM that the service is resuming.
SetServiceStatus(SERVICE_CONTINUE_PENDING);

// Perform service-specific continue operations.
OnContinue();

// Tell SCM that the service is running.
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
// Log the error.
WriteErrorLogEntry(L"Service Continue", dwError);

// Tell SCM that the service is still paused.
SetServiceStatus(SERVICE_PAUSED);
}
catch (...)
{
// Log the error.
WriteEventLogEntry(L"Service failed to resume.", EVENTLOG_ERROR_TYPE);

// Tell SCM that the service is still paused.
SetServiceStatus(SERVICE_PAUSED);
}
}


//
void CServiceBase::OnContinue()
{
}


//
void CServiceBase::Shutdown()
{
try
{
// 子类关机前做的操作.
OnShutdown();

// 告诉SCM停止服务
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
// Log the error.
WriteErrorLogEntry(L"Service Shutdown", dwError);
}
catch (...)
{
// Log the error.
WriteEventLogEntry(L"Service failed to shut down.", EVENTLOG_ERROR_TYPE);
}
}


//
void CServiceBase::OnShutdown()
{
}

#pragma endregion


#pragma region Helper Functions

//
// FUNCTION: CServiceBase::SetServiceStatus(DWORD, DWORD, DWORD)
//
// PURPOSE: 设置服务状态并告诉给SCM
//
// PARAMETERS:
// * dwCurrentState - 服务当前状态码
// * dwWin32ExitCode - 服务的错误退出码
// * dwWaitHint - 挂起服务的大概时间(毫秒)
//
void CServiceBase::SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;

// Fill in the SERVICE_STATUS structure of the service.
m_status.dwCurrentState = dwCurrentState;
m_status.dwWin32ExitCode = dwWin32ExitCode;
m_status.dwWaitHint = dwWaitHint;

m_status.dwCheckPoint =
((dwCurrentState == SERVICE_RUNNING) ||
(dwCurrentState == SERVICE_STOPPED)) ?
0 : dwCheckPoint++;

// Report the status of the service to the SCM.
::SetServiceStatus(m_statusHandle, &m_status);
}

//
// PURPOSE: 写日志到Windows应用程序日志中
//
// PARAMETERS:
// * pszMessage - 日志内容
// * wType - 日志类型,有以下几种:
//
// EVENTLOG_SUCCESS
// EVENTLOG_AUDIT_FAILURE
// EVENTLOG_AUDIT_SUCCESS
// EVENTLOG_ERROR_TYPE
// EVENTLOG_INFORMATION_TYPE
// EVENTLOG_WARNING_TYPE
//
void CServiceBase::WriteEventLogEntry(PWSTR pszMessage, WORD wType)
{
HANDLE hEventSource = NULL;
LPCWSTR lpszStrings[2] = { NULL, NULL };

hEventSource = RegisterEventSource(NULL, m_name);
if (hEventSource)
{
lpszStrings[0] = m_name;
lpszStrings[1] = pszMessage;

ReportEvent(hEventSource, // Event log handle
wType, // Event type
0, // Event category
0, // Event identifier
NULL, // No security identifier
2, // Size of lpszStrings array
0, // No binary data
lpszStrings, // Array of strings
NULL // No binary data
);

DeregisterEventSource(hEventSource);
}
}



// PURPOSE: 写错误日志到Windows 应用程序日志中
//
// PARAMETERS:
// * pszFunction - 错误信息
// * dwError - 错误码
//
void CServiceBase::WriteErrorLogEntry(PWSTR pszFunction, DWORD dwError)
{
wchar_t szMessage[260];
StringCchPrintf(szMessage, ARRAYSIZE(szMessage),
L"%s failed w/err 0x%08lx", pszFunction, dwError);
WriteEventLogEntry(szMessage, EVENTLOG_ERROR_TYPE);
}

#pragma endregion

根据以上代码,可以总结出以下图:

可以看到启动的流程,需要做一些准备操作,才可以告诉SCM,我启动成功了。

停止和暂停的流程,只需要报告给SCM就可以了。

现在的代码并没有什么业务,只是启动、停止。

我们可以在OnStart和OnStop里面写业务代码

我们写一个类,继承CServiceBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "ServiceBase.h"
#include <string>
using std::wstring;

class CImpService : public CServiceBase
{
public:

CImpService(PWSTR pszServiceName,
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE);
virtual ~CImpService(void);

protected:

virtual void OnStart(DWORD dwArgc, PWSTR *pszArgv);
virtual void OnStop();

private:

};
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

CImpService::CImpService(PWSTR pszServiceName,
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue)
: CServiceBase(pszServiceName, fCanStop, fCanShutdown, fCanPauseContinue)
{

}


CImpService::~CImpService(void)
{

}


void CImpService::OnStart(DWORD dwArgc, LPWSTR *lpszArgv)
{
// Log a service start message to the Application log.
WriteEventLogEntry(L"CImpService in OnStart",
EVENTLOG_INFORMATION_TYPE);
//业务代码
}

//
void CImpService::OnStop()
{
//业务代码
WriteEventLogEntry(L"CImpService in OnStop",
EVENTLOG_INFORMATION_TYPE);

}

服务的安装

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
void InstallService(PWSTR pszServiceName, 
PWSTR pszDisplayName,
DWORD dwStartType,
PWSTR pszDependencies,
PWSTR pszAccount,
PWSTR pszPassword)
{
wchar_t szPath[MAX_PATH];
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
//获取当前exe的路径
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0)
{
wprintf(L"GetModuleFileName failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}

// 打开SCM
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT |
SC_MANAGER_CREATE_SERVICE);
if (schSCManager == NULL)
{
wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}

// 安装服务
schService = CreateService(
schSCManager, // SCManager database
pszServiceName, // 服务名称
pszDisplayName, // 显示的名称
SERVICE_QUERY_STATUS, // 安全性访问权限
// 服务类型
// 如果是SERVICE_WIN32_OWN_PROCESS 或 SERVICE_WIN32_SHARE_PROCESS
// 且使用LocalSystem帐号来运行该服务程序,则还可以附加SERVICE_INTERACTIVE_PROCESS
//
SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
dwStartType, // 服务启动选项
SERVICE_ERROR_NORMAL, // 发生错误的保护措施
szPath, // 服务的二进制文件路径
NULL, // 加载服务组的名称,NULL不加载
NULL, // 不更改组现有的标签
pszDependencies, // 依赖的服务,为NULL代表没有依赖
pszAccount, // 服务的运行账号
pszPassword // 服务运行账号的密码
);
if (schService == NULL)
{
wprintf(L"CreateService failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}

wprintf(L"%s is installed.\n", pszServiceName);

Cleanup:
// Centralized cleanup for all allocated resources.
if (schSCManager)
{
CloseServiceHandle(schSCManager);
schSCManager = NULL;
}
if (schService)
{
CloseServiceHandle(schService);
schService = NULL;
}
}

服务的卸载

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
void UninstallService(PWSTR pszServiceName)
{
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
SERVICE_STATUS ssSvcStatus = {};

// 打开SCM
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (schSCManager == NULL)
{
wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}

//打开服务,带停止和查询以及删除权限
schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP |
SERVICE_QUERY_STATUS | DELETE);
if (schService == NULL)
{
wprintf(L"OpenService failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}

// 尝试停止服务
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus))
{
wprintf(L"Stopping %s.", pszServiceName);

//查询服务状态
while (QueryServiceStatus(schService, &ssSvcStatus))
{
//如果服务状态 等于 正在停止
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
wprintf(L".");

}
else break;//不等于则调出
}
//如果服务状态 等于 停止
if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED)
{
wprintf(L"\n%s is stopped.\n", pszServiceName);
}
else
{
//服务状态不等于停止
wprintf(L"\n%s failed to stop.\n", pszServiceName);
}
}

// 调用删除服务函数
if (!DeleteService(schService))
{
wprintf(L"DeleteService failed w/err 0x%08lx\n", GetLastError());
goto Cleanup;
}

wprintf(L"%s is removed.\n", pszServiceName);

Cleanup:
// Centralized cleanup for all allocated resources.
if (schSCManager)
{
CloseServiceHandle(schSCManager);
schSCManager = NULL;
}
if (schService)
{
CloseServiceHandle(schService);
schService = NULL;
}
}

更新服务描述

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
/**
* 更新(添加)服务描述。
* @param serviceName : 服务名称。
* @param serviceDescription : 服务描述。
*/
bool updateServiceDescription(LPTSTR serviceName, LPTSTR serviceDescription)
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
SERVICE_DESCRIPTION sd;
//LPTSTR szDesc = TEXT(serviceDescription);

// 打开SCM
schSCManager = ::OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights

if (NULL == schSCManager)
{
//printf("OpenSCManager failed (%d)\n", GetLastError());
return false;
}

// 获取服务句柄
schService = ::OpenService(
schSCManager, // SCM database
serviceName, // name of service
SERVICE_CHANGE_CONFIG); // need change config access

if (schService == NULL)
{
//printf("OpenService failed (%d)\n", GetLastError());
::CloseServiceHandle(schSCManager);
return false;
}

// 改变服务描述
sd.lpDescription = serviceDescription;

if (!::ChangeServiceConfig2(
schService, // handle to service
SERVICE_CONFIG_DESCRIPTION, // change: description
&sd)) // new description
{
//printf("ChangeServiceConfig2 failed\n");
::CloseServiceHandle(schService);
::CloseServiceHandle(schSCManager);
return false;
}
//else printf("Service description updated successfully.\n");

::CloseServiceHandle(schService);
::CloseServiceHandle(schSCManager);
return true;
}

结尾

Windows服务C++版就到这里,稍加改造便可以在项目中使用。

参考文献

  1. Microsoft Docs:开发 Windows 服务应用

  2. 百度百科:LOCAL SERVICE

  3. Microsoft Docs:服务账号

  4. Microsoft Docs:CreateServiceA

  5. Microsoft Docs:Service

评论