将Windows控制台程序改造为Windows服务程序

一、改造main入口

  main()函数仅负责创建服务分配表并且启动控制分派机制,程序的入口还是和普通控制台程序一样,但是建议不要在main函数中直接编写服务的程序逻辑,因为如果不能尽快的开启派遣并完成服务注册的话,服务控制管理器最终会强制终止程序。改造号的main函数主要用于启动服务入口派遣,StartServiceCtrlDispatcher()为SERVICE_TABLE_ENTRY的每一项开启一个线程,并且监视它们的运行状态,只有当所有的服务入口函数/线程退出时,StartServiceCtrlDispatcher()才返回。

(1).SERVICE_TABLE_ENTRY结构体

SERVICE_TABLE_ENTRY 结构体是用于在 Windows 服务控制程序中定义服务的主要入口点(ServiceMain 函数)和服务名的结构体。它通常用于服务的注册和启动。

以下是关于 SERVICE_TABLE_ENTRY 结构体的介绍和使用: 

1
2
3
4
typedef struct _SERVICE_TABLE_ENTRY {
    LPTSTR lpServiceName;
    LPSERVICE_MAIN_FUNCTION lpServiceProc;
} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;

参数说明:

  • lpServiceName:这是一个指向以 null 结尾的字符串的指针,用于指定服务的名称。服务名称是在注册服务时用于标识服务的唯一字符串。它需要与服务控制管理器中注册的服务名称匹配。

  • lpServiceProc:这是一个指向 SERVICE_MAIN_FUNCTION 的指针,它是一个函数指针,指向服务的主要入口点函数(ServiceMain 函数)。SERVICE_MAIN_FUNCTION 是一个特殊的函数签名,用于指定服务的主要操作。该函数通常负责初始化服务并进入服务的主要处理循环。

(2).StartServiceCtrlDispatcher 函数

StartServiceCtrlDispatcher 是 Windows API 中用于启动服务控制分发器线程的函数。服务控制分发器线程用于等待来自服务控制管理器的控制请求,并将这些请求分派到已注册的服务的控制处理程序上。当 SCM 启动某个服务时,它等待某个进程的主线程来调用 StartServiceCtrlDispatcher 函数,将分派表传递给 StartServiceCtrlDispatcher。这将把调用进程的主线程转换为控制分派器。该分派器启动一个新线程,该线程运行分派表中每个服务的 ServiceMain 函数分派器还监视程序中所有服务的执行情况。然后分派器将控制请求从 SCM 传给服务。分派表中所有的服务执行完之后,或者发生错误时。StartServiceCtrlDispatcher 调用返回。然后主进程终止。SCM 开始启动一个服务程序时,总是等待这个服务程序去调用 StartServiceCtrlDispatcher()。而当服务开始运行时,main() 将会调用 ServiceMain(), 直到ServiceMain() 执行完毕或发生错误而退出,StartServiceCtrlDispatcher() 返回,主线程将会终止。

以下是关于 StartServiceCtrlDispatcher 函数的介绍和使用:

1
2
3
BOOL StartServiceCtrlDispatcher(
  const SERVICE_TABLE_ENTRY *lpServiceTable
);

参数说明:

  • lpServiceTable:这是一个指向 SERVICE_TABLE_ENTRY 数组的指针,该数组包含了已注册服务的信息,包括服务的名称和服务的主要入口点函数。通常,最后一个元素的 lpServiceNamelpServiceProc 都为 NULL,以表示服务表的结束。

返回值:

  • 如果函数成功启动服务控制分发器线程,它将返回 TRUE。如果失败,它将返回 FALSE,并且可以使用 GetLastError() 获取错误代码。  

下面是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <Windows.h>
 
void ServiceMain(int argc, char* argv[]);
 
int main() {
    SERVICE_TABLE_ENTRY serviceTable[] =
    {
        { "MyServiceName", ServiceMain },
        { NULL, NULL }
    };
 
    if (!StartServiceCtrlDispatcher(serviceTable)) {
        // Handle error
        return 1;
    }
 
    return 0;
}
 
void ServiceMain(int argc, char* argv[]) {
    // 这里是服务的主要入口点,用于初始化服务并进入服务的主要处理循环
    // 可以在这里执行服务的初始化和处理逻辑
}

二、编写服务入口函数

(1).ServiceMain函数

ServiceMain 函数是在Windows操作系统中用于服务应用程序的入口点。它的主要作用是启动和管理服务应用程序的生命周期。在Windows中,服务是一种特殊类型的应用程序,通常在后台运行,以执行一些系统任务或提供某种功能,例如打印服务、Web服务器等。

以下是 ServiceMain 函数的主要作用:

  • 初始化服务:ServiceMain 函数在服务应用程序启动时被调用,它通常用于执行初始化操作,例如设置日志、配置参数、创建线程或资源等。

  • 启动服务控制管理器(SCM):服务应用程序必须注册自己到服务控制管理器,以便由SCM来管理和控制服务的启动、停止、暂停、继续等操作。ServiceMain 函数会通知SCM服务已经启动,并在这之后接受SCM的命令。

  • 响应服务控制命令:一旦服务启动,它就可以响应来自SCM的服务控制命令,例如停止服务、暂停服务、继续服务等。ServiceMain 函数通常会包含一个主循环,以便处理这些命令。

  • 执行服务主要功能:ServiceMain 函数通常会启动一个或多个线程,用于执行服务的主要功能。这些功能可以是周期性的任务,也可以是长时间运行的后台进程,取决于服务的具体需求。

  • 关闭和清理:当服务被停止时,ServiceMain 函数会负责执行清理操作,释放资源,并通知SCM服务已经停止。

(2).RegisterServiceCtrlHandler函数

RegisterServiceCtrlHandler 是 Windows API 中用于注册服务控制处理程序的函数。服务控制处理程序是一个用户定义的回调函数,用于处理来自服务控制管理器的控制请求,例如启动、停止、暂停、继续等。

下面是关于 RegisterServiceCtrlHandler 函数的介绍和使用:  

1
2
3
4
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(
  LPCWSTR                lpServiceName,
  LPHANDLER_FUNCTION_EX  lpHandlerProc
);

参数说明:

  • lpServiceName:这是一个以 null 结尾的字符串,用于指定要注册的服务的名称。服务名称需要与服务控制管理器中注册的服务名称匹配。

  • lpHandlerProc:这是一个指向服务控制处理程序的回调函数的指针。该函数通常有以下签名:

1
void WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext);
  • dwControl:指示服务控制请求的值,例如 SERVICE_CONTROL_STOPSERVICE_CONTROL_PAUSE 等。

  • dwEventType:指示控制请求的事件类型,通常为 0。

  • lpEventData:用于传递控制请求的事件数据,通常为 NULL。

  • lpContext:可以用于传递自定义上下文信息的指针,通常为 NULL。

返回值:

  • 如果注册成功,RegisterServiceCtrlHandler 将返回一个 SERVICE_STATUS_HANDLE,它是一个用于标识服务控制处理程序的句柄。这个句柄将在后续的服务控制请求处理中使用。

以下是一个示例,演示如何使用 RegisterServiceCtrlHandler 注册服务控制处理程序:

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
#include <Windows.h>
 
// 服务控制处理程序
void WINAPI MyHandler(DWORD dwControl) {
    switch (dwControl) {
        case SERVICE_CONTROL_STOP:
            // 处理停止服务请求
            break;
        case SERVICE_CONTROL_PAUSE:
            // 处理暂停服务请求
            break;
        // 其他控制请求的处理
    }
}
 
int main() {
    SERVICE_STATUS_HANDLE hStatus;
    hStatus = RegisterServiceCtrlHandler(L"MyServiceName", MyHandler);
 
    if (hStatus == NULL) {
        // 处理注册失败
        return 1;
    }
 
    // 在此处可以执行服务初始化逻辑
 
    // 进入服务主要处理循环
 
    return 0;
}

(3).SERVICE_STATUS结构体

1
2
3
4
5
6
7
8
9
typedef struct _SERVICE_STATUS {
  DWORD dwServiceType;   // 服务类型。
  DWORD dwCurrentState;   // 服务的当前状态。
  DWORD dwControlsAccepted;  // 服务在其处理函数中接受和处理的控制代码。
  DWORD dwWin32ExitCode;    // 服务用于报告启动或停止时发生错误的错误代码。
  DWORD dwServiceSpecificExitCode;  // 服务在启动或停止时发生错误时返回的错误代码。
  DWORD dwCheckPoint;   // 服务在长时间启动、停止、暂停或继续操作期间定期递增以报告其进度的检查点值。
  DWORD dwWaitHint;    // 将要进行 开始、停止、暂停或继续服务 操作所需的估计时间 (以毫秒为单位)。
} SERVICE_STATUS, *LPSERVICE_STATUS;

dwServiceType 的值:

 dwCurrentState的值:

 dwControlsAccepted的值:

 (4).SetServiceStatus() 函数

SetServiceStatus 函数是用于向服务控制管理器(Service Control Manager,SCM)报告服务的当前状态的 Windows API 函数。它通常在 Windows 服务的主要入口点函数(ServiceMain)中使用,以便通知 SCM 有关服务的状态更改。SetServiceStatus 函数的主要作用是向 SCM 发送有关服务状态的信息,以便 SCM 可以了解并管理服务的状态。

以下是 SetServiceStatus 函数的基本介绍:

1
2
3
4
BOOL SetServiceStatus(
  SERVICE_STATUS_HANDLE hServiceStatus,
  LPSERVICE_STATUS      lpServiceStatus
);

参数说明:

  • hServiceStatus:这是一个服务状态句柄,它是在调用 RegisterServiceCtrlHandler 函数时返回的。该句柄用于标识要报告状态的服务。

  • lpServiceStatus:这是一个指向 SERVICE_STATUS 结构体的指针,包含了服务的状态信息。SERVICE_STATUS 结构体定义如下:

三、安装服务

修改main()入口函数,通过对参数的处理。-register参数表示安装服务,-unregister表示移除服务,-run表示以控制台的方式运行

下面是启动服务的完整代码:

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
#include <Windows.h>
#include <tlhelp32.h>
#include <userenv.h>
#include <wtsapi32.h>
#include <tchar.h>
#include <stdio.h>
#include <iostream>
 
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE hStatus;
 
TCHAR SERVICE_NAME[] = TEXT("RemoteService");
 
void WINAPI ServiceHandler(DWORD fdwControl) {
    switch (fdwControl) {
    case SERVICE_CONTROL_PAUSE:
        serviceStatus.dwCurrentState = SERVICE_PAUSED;
        break;
    case SERVICE_CONTROL_CONTINUE:
        serviceStatus.dwCurrentState = SERVICE_RUNNING;
        break;
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:    
        serviceStatus.dwCurrentState = SERVICE_STOPPED;
        serviceStatus.dwCheckPoint = 0;
        serviceStatus.dwWaitHint = 0;
        SetServiceStatus(hStatus, &serviceStatus);
        return;
    default:
        break;
    }
    SetServiceStatus(hStatus, &serviceStatus); // 重设服务状态。
 
    return;
}
 
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv) {
    //注册控制函数
    hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceHandler);
    if (!hStatus) {
        std::cout << "RegisterServiceCtrlHandler Failed:" << GetLastError() << std::endl;
 
        return;
    }
 
    //初始化状态设置
    serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    //即服务目前状态:正在初始化
    serviceStatus.dwCurrentState = SERVICE_START_PENDING;
    //通知SCM服务接收:停止、暂停、继续、关机等命令
    serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
    serviceStatus.dwWin32ExitCode = 0;
    serviceStatus.dwCheckPoint = 0;
    serviceStatus.dwServiceSpecificExitCode = 0;
    serviceStatus.dwWaitHint = 0;
 
    if (!SetServiceStatus(hStatus, &serviceStatus)) {
        // 如果出错,将其重设为停止状态。
        serviceStatus.dwCurrentState = SERVICE_STOPPED;
        serviceStatus.dwCheckPoint = 0;
        serviceStatus.dwWaitHint = 0;
        serviceStatus.dwWin32ExitCode = GetLastError();
        SetServiceStatus(hStatus, &serviceStatus);
 
        std::cout << "SetServiceStatus  set SERVICE_START_PENDING is failed:" << GetLastError() << std::endl;
 
        return;
    }
 
    //设置运行状态
    serviceStatus.dwCurrentState = SERVICE_RUNNING;
    if (!SetServiceStatus(hStatus, &serviceStatus)) {
        serviceStatus.dwCurrentState = SERVICE_STOPPED;
        serviceStatus.dwWin32ExitCode = GetLastError();
        std::cout << "SetServiceStatus  set SERVICE_RUNNING is failed:" << GetLastError() << std::endl;
 
        return;
    }
 
    //服务启动后执行逻辑
    if (serviceStatus.dwCurrentState == SERVICE_RUNNING) {
        STARTUPINFO si;
        PROCESS_INFORMATION pi;
 
        ZeroMemory(&si, sizeof(si));
        ZeroMemory(&pi, sizeof(pi));
 
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = FALSE;
 
        TCHAR sFilePath[256] = { 0 };
        GetModuleFileName(NULL, sFilePath, sizeof(sFilePath));
        _tcscat_s(sFilePath, MAX_PATH, TEXT(" -run"));
 
        if (!CreateProcess(NULL, sFilePath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
 
            std::cout << "SetServiceStatus  CreateProcess is failed:" << GetLastError() << std::endl;
        }
 
        WaitForSingleObject(pi.hProcess, 100);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
}
 
bool registerService(const TCHAR* serviceName) {
    SC_HANDLE       hScm;
    SC_HANDLE       hSrv;
    SERVICE_STATUS  InstallStatus;
    DWORD           dwErrCode;
 
    //获取服务完整路径
    TCHAR sFilePath[256] = { 0 };
    GetModuleFileName(NULL, sFilePath, sizeof(sFilePath));
 
    // 打开服务管理器
    hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hScm == NULL) {
        std::cout << "registerService OpenSCManager() failed" << std::endl;
        return false;
    }
 
    // 尝试创建服务
    hSrv = CreateService(hScm, serviceName, serviceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
        SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, sFilePath, NULL, NULL, NULL, NULL, NULL);
    if (hSrv == NULL) {
        dwErrCode = GetLastError();
        if (dwErrCode != ERROR_SERVICE_EXISTS) {
            std::cout << "registerService OreateService() failed:" << dwErrCode <<  std::endl;
            CloseServiceHandle(hScm);
            return false;
        }
 
        // 尝试打开服务
        hSrv = OpenService(hScm, serviceName, SERVICE_ALL_ACCESS);
        if (hSrv == NULL) {
            std::cout << "registerService OpenService() failed:" << GetLastError() << std::endl;         
            CloseServiceHandle(hScm);
            return false;
        }
    }
 
    // 启动服务
    if (StartService(hSrv, 0, NULL) == 0) {
        dwErrCode = GetLastError();
        if (dwErrCode == ERROR_SERVICE_ALREADY_RUNNING) {
            std::cout << "registerService StartService() already Running:" << dwErrCode << std::endl;
            CloseServiceHandle(hScm);
            CloseServiceHandle(hSrv);
            return false;
        }
    }
 
    // 查询服务状态
    while (QueryServiceStatus(hSrv, &InstallStatus) != 0) {
        if (InstallStatus.dwCurrentState != SERVICE_START_PENDING)
            break;
 
        Sleep(100);
    }
 
    if (InstallStatus.dwCurrentState != SERVICE_RUNNING) {
        std::cout << "registerService StartService() StartService failed" << std::endl;
    } else {
        std::cout << "registerService StartService() StartService success" << std::endl;
    }
 
    CloseServiceHandle(hScm);
    CloseServiceHandle(hSrv);
 
    return true;
}
 
bool unRegisterService(const TCHAR* serviceName) {
    SC_HANDLE        hScm;
    SC_HANDLE        hSrv;
    SERVICE_STATUS   RemoveStatus;
 
    // 打开服务管理器
    hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hScm == NULL) {
        std::cout << "unRegisterService OpenSCManager() failed:" << GetLastError() << std::endl;
        return false;
    }
 
    // 尝试打开服务
    hSrv = OpenService(hScm, serviceName, SERVICE_ALL_ACCESS);
    if (hSrv == NULL) {
        std::cout << "unRegisterService OpenService() failed:" << GetLastError() << std::endl;
        CloseServiceHandle(hScm);
        return false;
    }
 
    // 查询服务状态
    if (QueryServiceStatus(hSrv, &RemoveStatus) != 0) {
        if (RemoveStatus.dwCurrentState == SERVICE_STOPPED) {
            std::cout << "unRegisterService QueryServiceStatus() already Stopped " << std::endl;
        } else {
            // 尝试停止服务
            if (ControlService(hSrv, SERVICE_CONTROL_STOP, &RemoveStatus) != 0) {
                while (RemoveStatus.dwCurrentState == SERVICE_STOP_PENDING) {
                    Sleep(10);
                    QueryServiceStatus(hSrv, &RemoveStatus);                 
                }
 
                if (RemoveStatus.dwCurrentState == SERVICE_STOPPED) {
                    std::cout << "StopService Success" << std::endl;
                } else {
                    std::cout << "unRegisterService ControlService() StopService failed" << std::endl;
                }
            } else {
                std::cout << "unRegisterService ControlService() StopService failed" << std::endl;
            }
        }
    }
    else {
        std::cout << "unRegisterService QueryServiceStatus() Query failed" << std::endl;
    }
 
    // 删除服务
    if (DeleteService(hSrv) == 0) {
        std::cout << "unRegisterService DeleteService() DeleteService failed" << std::endl;
    }
    else {
        std::cout << "unRegisterService DeleteService() DeleteService Success" << std::endl;
    }
 
    CloseServiceHandle(hScm);
    CloseServiceHandle(hSrv);
 
    return true;
}
 
int main(int argc, char* argv[]) {
    if (argc >= 2) {
        if (strcmp(argv[1], "-register") == 0) {
            //注册服务
            registerService(SERVICE_NAME);
        } else if (strcmp(argv[1], "-unregister") == 0) {
            //移除服务
            unRegisterService(SERVICE_NAME);
        } else if (strcmp(argv[1], "-run") == 0) {
            //正常运行
            std::cout << "run service on console" << std::endl;        
            system("pause");
        }
    }
    else {
        OutputDebugString(TEXT("start service"));
        SERVICE_TABLE_ENTRY ServTable[2];
        ServTable[0].lpServiceName = SERVICE_NAME;
        ServTable[0].lpServiceProc = ServiceMain;
        ServTable[1].lpServiceName = NULL;
        ServTable[1].lpServiceProc = NULL;
        if (!StartServiceCtrlDispatcher(ServTable)) {
            std::cout << "StartServiceCtrlDispatcher:" << GetLastError() << std::endl;
        }
    }
 
    return 1;
}

  

 

posted @   TechNomad  阅读(150)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示