将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
数组的指针,该数组包含了已注册服务的信息,包括服务的名称和服务的主要入口点函数。通常,最后一个元素的lpServiceName
和lpServiceProc
都为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_STOP
、SERVICE_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; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?