当前位置:C++技术网 > 精选软件 > C++编写Windows服务程序基本流程代码封装

C++编写Windows服务程序基本流程代码封装

更新时间:2016-11-17 20:42:28浏览次数:1+次

    Windows的服务程序是后台运行的一种程序,和我们普通的前台运行程序,还是有所不同。如果我们要让程序长期运行在后台,写成服务程序是比较好的选择。
    服务程序也是需要安装的,这就是我们听说的安装服务。服务也是可以删除的。当然,服务程序还有状态,就和我们前台程序一样,可以是挂起、运行、停止。服务程序由sc.exe程序来管理。我们在命令行中敲的sc开始的命令也就是管理服务的命令哦。
    那么我们一开始写服务程序,还是一片茫然吧。服务程序的代码写在哪里、如何写、服务如何安装、服务如何删除、服务如何启动、服务如何停止,这些对于我们前台程序员来说,似乎都挺陌生的。没事,我们这里一一解答。
    先从服务使用开始说,让你慢慢进入状态。
    服务的几个操作命令:
sc query 服务名     :查询服务的状态
sc start 服务名       :启动服务,需要服务已经被安装
sc stop 服务名       :停止服务,需要服务已经被安装
sc delete 服务名     :删除服务,需要服务已经被安装
    对于以上4个命令,其实有些人还是接触过的。看上去也是挺眼熟的。然而,服务是如何被安装进去的呢?我们平时安装服务程序的时候,都是一键安装,然后服务就自己安装好了。那么手动安装,就懵逼了。
sc create 服务名 binpath= 服务程序exe的地址   :注意,binpath=后面一定要有一个空格,不然安装会失败,地址就是文件地址,如D:/service_my.exe 。
    也就是说,我们只要执行这么一个命令,就可以安装这个服务了。而服务程序本身也是exe文件。这么一来,好像也不是那么复杂。我们敲命令来操作服务的状态,以及安装和卸载服务,就和我们平时双击启动程序、关闭程序、安装程序、卸载程序是差不多的。只是服务嘛,属于后台程序,一般都是没有界面的,所以也就通常用命令来操作比较方便。
    好了,现在熟悉了服务程序是怎么一回事了,那么服务程序是如何写的呢?

    我们一般的控制台程序,都是从exe开始的,入口函数是main。那么服务程序的入口函数呢?

    其实,我们的控制台程序的main函数名字也不是固定的,你也可以修改。而服务程序要进入入口函数前,你要先配置一下。有一个结构体叫做SERVICE_TABLE_ENTRY,你给这个结构体变量赋值,指定服务的入口函数,即回调函数。然后启动服务控制分发器,这样就可以让服务入口函数被识别了。

    所以,我们写的服务程序,得先运行起来,然后再启动服务控制分发器,然后让服务运行起来。我们开始运行的程序,写成控制台程序就可以了。
    那么启动服务控制分发器在main函数中执行。关键代码片段如下:


SERVICE_TABLE_ENTRY entrytable[2];
entrytable[0].lpServiceName=m_ServiceName;
entrytable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain;//服务程序入口函数
entrytable[1].lpServiceName=NULL;
entrytable[1].lpServiceProc=NULL;
StartServiceCtrlDispatcher(entrytable);//启动服务控制分发器
    进而,我们就需要写服务程序入口函数了。在入口函数里,我们要注册下控制回调函数,就和窗口的回调函数差不多。这样在服务状态被改变的时候,可以通知我们处理。注册好后,就可以设置服务状态。然后就是死循环的执行服务程序代码。和线程函数有点像。控制回调函数和窗口过程函数有点像。反正就是这么一个意思。


    那么入口函数的代码如下:


m_ServiceStatus.dwServiceType = SERVICE_WIN32;
//初始化前状态设置为挂起状态
m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
//只接受关机和停止服务两种控制命令
m_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_STOP;
//设置为0的下面几句代码可以不写,因为结构体初始化时已经设置为0了,不过这里为了展示设置选项写了一下。
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwServiceSpecificExitCode = 0;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
//注册控制回调函数
m_hStatus = RegisterServiceCtrlHandler(m_ServiceName, CtrlHandler);
if (!m_hStatus)return;//服务注册失败
m_is_run=true;
//初始化完后,切换服务运行状态
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (m_hStatus, &m_ServiceStatus);

//服务执行的程序的内容
while (m_is_run)
{
    //这里写服务代码
}
    前面准备好后,在循环里写服务代码即可。然后就是要准备控制回调函数,回调函数如下:



switch (request)
{
case SERVICE_CONTROL_STOP:
    g_is_run=false;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    break;
case SERVICE_CONTROL_SHUTDOWN:
    g_is_run=false;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    break;
default:
    break;
}
SetServiceStatus (g_hStatus, &g_ServiceStatus);
    其实关键的函数也就是SetServiceStatus,修改服务状态。其实就没有什么。


    说到这里,一个简单的服务程序模型就写好了。下面给完整的封装在类里面的代码1.h:


#include <Windows.h>
#include "iostream"
using namespace std;

string g_ServiceName="myservice";
bool g_is_run=false;
SERVICE_STATUS_HANDLE g_hStatus=0;
SERVICE_STATUS g_ServiceStatus={0};

class ServiceManager
{
public:
    ServiceManager(){}
    ~ServiceManager(){}
    static void WINAPI CtrlHandler(DWORD request)
    {
        switch (request)
        {
        case SERVICE_CONTROL_STOP:
            g_is_run=false;
            g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            break;
        case SERVICE_CONTROL_SHUTDOWN:
            g_is_run=false;
            g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            break;
        default:
            break;
        }
        SetServiceStatus (g_hStatus, &g_ServiceStatus);
    }
    static void WINAPI ServiceMain(int argc, char** argv)
    {
        g_ServiceStatus.dwServiceType = SERVICE_WIN32;
        //初始化前状态设置为挂起状态
        g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
        //只接受关机和停止服务两种控制命令
        g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_STOP;
        //设置为0的下面几句代码可以不写,因为结构体初始化时已经设置为0了,不过这里为了展示设置选项写了一下。
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwServiceSpecificExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 0;
        g_ServiceStatus.dwWaitHint = 0;
        //注册控制回调函数
        g_hStatus = RegisterServiceCtrlHandler(g_ServiceName.c_str(), CtrlHandler);
        if (!g_hStatus)return;//服务注册失败
        g_is_run=true;
        //初始化完后,切换服务运行状态
        g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus (g_hStatus, &g_ServiceStatus);

        //服务执行的程序的内容
        while (g_is_run)
        {
            //这里写服务代码
        }
    }
    static DWORD WINAPI RunService()
    {
        //服务线程
        SERVICE_TABLE_ENTRY entrytable[2];
        entrytable[0].lpServiceName=(LPSTR)g_ServiceName.c_str();
        entrytable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain;//服务程序入口函数
        entrytable[1].lpServiceName=NULL;
        entrytable[1].lpServiceProc=NULL;
        StartServiceCtrlDispatcher(entrytable);//启动服务控制消息分发器
        return 0;
    }
};
    在main函数中启动服务程序的代码如下:



#include "1.h"
void main()
{
    ServiceManager::RunService();
}
    将这个控制台程序编译生成exe文件,这个exe文件就是服务程序了。然后用前面的sc create命令将这个服务程序安装到系统,就大工告成了。


   是不是很简单呀?!