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

浙公网安备 33010602011771号