C++ windows服务开发

新项目有一个服务需要作为windows服务运行在后台,并且需要守护。但是没有弄过windows的服务,找了一些资料,现记录一下备忘。

当做服务启动

并不是所有的可执行程序都可以作为服务启动,使用C语言或者C++开发的程序需要按照windows的服务开发规范,编译成可执行程序之后才能作为服务。

C#语言可以在Visual Studio上可以直接创建windows服务项目。

如果随便把一个程序创建成服务,会出现如下错误:

其实也有现成工具可以直接将一个可执行程序直接打包成服务,或者引入三方库也可以,但是总归是有依赖。

示例代码

#include <Windows.h>

#define SERVICE_NAME "ServiceTest"

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE ServiceStatusHandle;

void WINAPI ServiceStatusHandler(DWORD control){
    switch (control){
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:
        ServiceStatus.dwWin32ExitCode = 0;
        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;
        SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
        break;
    }
}

void WINAPI service_main(int argc, char ** argv){
    ServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceStatusHandler);
    if(ServiceStatusHandle == NULL){
        return;
    }
    //
    // user code here
    //
    ServiceStatus.dwServiceType = SERVICE_WIN32;  
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_PAUSE_CONTINUE;
    ServiceStatus.dwWin32ExitCode = NO_ERROR;
    ServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;

    SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
}

int main(int argc, char ** argv){
    SERVICE_TABLE_ENTRY serviceTable[2];
    serviceTable[0].lpServiceName = SERVICE_NAME;
    serviceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)service_main;
    serviceTable[1].lpServiceName = NULL;
    serviceTable[1].lpServiceProc = NULL;

    StartServiceCtrlDispatcher(serviceTable);
    return 0;
}

主要需要三个步骤:

  1. StartServiceCtrlDispatcher注册服务入口,包含服务名称lpServiceName和服务启动入口lpServiceProc函数指针,注意参数是一个数组,而且数组一定要以NULL结尾;
  2. RegisterServiceCtrlHandler注册服务状态改变处理函数,主要处理服务的关闭、暂停、继续等状态操作。
  3. SetServiceStatus设置服务状态,比如运行中、停止、暂停等状态,需要手工设置,直接体现在windows的服务窗口上。

用户的业务初始化代码最好放在ServiceMain函数中,初始化完毕之后设置服务状态为运行中,服务才能启动成功。

作为服务运行

服务需要用到命令sc.exe,并且需要管理员权限。powershell找不到此命令。

创建服务

创建服务需要用到sc命令,简单创建一个服务命令如下:

sc create your-service-name binpath="your program path" start=auto
  • your-service-name是需要创建的服务的名称。
  • binpath是关联的可执行程序的绝对路径,可以在路径后面添加参数,但是参数只能传递到main函数,不能到达ServiceMain,需要手动保存才可以。
  • start指的是启动方式,可以是auto自动启动、demand手动启动、disable禁用等。

创建成功之后,运行win+R,输入services.msc,打开之后就可以找到你创建的服务,如果一切正常就可以启动成功了。

启动命令:

sc start your-service-name

开启服务守护

如果程序崩溃,服务可以自动启动。右键你的服务,然后在恢复标签里面选择第一次失败第二次失败后续失败后的行为,如果只是需要在程序崩溃后重新启动,选择重新启动程序即可。下面可以设置失败后多久后重启,设置0分钟会在程序奔溃后立即重启。

也可以在命令行中设置服务守护,输入如下命令:

sc failure your-service-name reset=0 actions=restart/3000/restart/3000/restart/3000
  • reset是失败多少次后重置失败次数。
  • actions后面的三个restart分别是第一次失败第二次失败后续失败后的行为,除了restart还可以是run或者reboot,后面的数字是执行时间ms。使用命令设置的时间粒度比设置界面上更细。

注意actions需要和reset一起使用,具体说明见官方文档:sc failure.

删除服务

执行一下命令即可:

sc delete your-service-name

Ref

https://blog.csdn.net/chenyujing1234/article/details/8023816