将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 数组的指针,该数组包含了已注册服务的信息,包括服务的名称和服务的主要入口点函数。通常,最后一个元素的 lpServiceNamelpServiceProc 都为 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_STOPSERVICE_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;
}

  

 

posted @ 2023-09-12 19:38  TechNomad  阅读(208)  评论(0)    收藏  举报