将Windows控制台程序改造为Windows服务程序
一、改造main入口
main()函数仅负责创建服务分配表并且启动控制分派机制,程序的入口还是和普通控制台程序一样,但是建议不要在main函数中直接编写服务的程序逻辑,因为如果不能尽快的开启派遣并完成服务注册的话,服务控制管理器最终会强制终止程序。改造号的main函数主要用于启动服务入口派遣,StartServiceCtrlDispatcher()为SERVICE_TABLE_ENTRY的每一项开启一个线程,并且监视它们的运行状态,只有当所有的服务入口函数/线程退出时,StartServiceCtrlDispatcher()才返回。
(1).SERVICE_TABLE_ENTRY结构体
SERVICE_TABLE_ENTRY
结构体是用于在 Windows 服务控制程序中定义服务的主要入口点(ServiceMain 函数)和服务名的结构体。它通常用于服务的注册和启动。
以下是关于 SERVICE_TABLE_ENTRY
结构体的介绍和使用:
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
函数的介绍和使用:
BOOL StartServiceCtrlDispatcher( const SERVICE_TABLE_ENTRY *lpServiceTable );
参数说明:
lpServiceTable
:这是一个指向SERVICE_TABLE_ENTRY
数组的指针,该数组包含了已注册服务的信息,包括服务的名称和服务的主要入口点函数。通常,最后一个元素的lpServiceName
和lpServiceProc
都为NULL
,以表示服务表的结束。
返回值:
- 如果函数成功启动服务控制分发器线程,它将返回
TRUE
。如果失败,它将返回FALSE
,并且可以使用GetLastError()
获取错误代码。
下面是一个简单的示例:
#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
函数的介绍和使用:
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler( LPCWSTR lpServiceName, LPHANDLER_FUNCTION_EX lpHandlerProc );
参数说明:
-
lpServiceName
:这是一个以 null 结尾的字符串,用于指定要注册的服务的名称。服务名称需要与服务控制管理器中注册的服务名称匹配。 -
lpHandlerProc
:这是一个指向服务控制处理程序的回调函数的指针。该函数通常有以下签名:
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
注册服务控制处理程序:
#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结构体
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
函数的基本介绍:
BOOL SetServiceStatus( SERVICE_STATUS_HANDLE hServiceStatus, LPSERVICE_STATUS lpServiceStatus );
参数说明:
-
hServiceStatus
:这是一个服务状态句柄,它是在调用RegisterServiceCtrlHandler
函数时返回的。该句柄用于标识要报告状态的服务。 -
lpServiceStatus
:这是一个指向SERVICE_STATUS
结构体的指针,包含了服务的状态信息。SERVICE_STATUS
结构体定义如下:
三、安装服务
修改main()入口函数,通过对参数的处理。-register参数表示安装服务,-unregister表示移除服务,-run表示以控制台的方式运行
下面是启动服务的完整代码:
#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; }