zoukankan      html  css  js  c++  java
  • C语言开发Windows服务

    参考:https://blog.csdn.net/mpp_king/article/details/79544721
    服务是一个运行在后台并且无需用户交互的控制台程序。
    可以通过在“开始->运行”中输入services.msc,打开系统的服务并进行配置。
     
    # 第一步:    主函数和全局定义
    首先,包含所需的头文件:
    #include <Windows.h> //调用 Win32 函数
    #include <stdio.h>//磁盘文件写入

    然后,定义两个常量:

    #define SLEEP_TIME 5000
    #define LOGFILE "D://project//c++//services//Debug//memstatus.txt" //exe文件所在文件夹
    SLEEP_TIME指定两次连续查询可用内存之间的毫秒间隔,在编写服务循环时需要用到这个常量。
    LOGFILE 定义日志文件的路径,你将会用 WriteToLog 函数将内存查询的结果输出到该文件,WriteToLog 函数定义如下: 
    int WriteToLog(char* str)
    {
        FILE* log;
        log = fopen(LOGFILE, "a+");
        if (log == NULL)
        return -1;
        fprintf(log, "%s/n", str);
        fclose(log);
        return 0;
    }
    声明几个全局变量,以便在程序的多个函数之间共享它们值。此外,做一个函数的前向定义:
    SERVICE_STATUS ServiceStatus; 
    SERVICE_STATUS_HANDLE hStatus; 
     
    void ServiceMain(int argc, char** argv); 
    void ControlHandler(DWORD request); 
    int InitService();
    服务程序是控制台程序的一个子集,因此需要定义一个main函数,它是控制台程序的入口点。服务程序的控制台程序的main函数非常简短,因为它只创建分派表并启动控制分派机。
    int _tmain(int argc, _TCHAR* argv[])
    {
        SERVICE_TABLE_ENTRY ServiceTable[2];
        ServiceTable[0].lpServiceName = "MemoryStatus";
        ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
     
     
        ServiceTable[1].lpServiceName = NULL;
        ServiceTable[1].lpServiceProc = NULL;
     
        StartServiceCtrlDispatcher(ServiceTable); 
    }
    一个程序可能包含若干个服务。每一个服务都必须列于专门的分派表中(为此该程序定义了一个 ServiceTable 结构数组)。这个表中的每一项都要在 SERVICE_TABLE_ENTRY 结构之中。它有两个域: 
    lpServiceName: 指向表示服务名称字符串的指针;当定义了多个服务时,那么这个域必须指定; 
    lpServiceProc: 指向服务主函数的指针(服务入口点); 
    分派表的最后一项必须是服务名和服务主函数域的 NULL 指针,文本例子程序中只宿主一个服务,所以服务名的定义是可选的。
    # 第二步:    ServiceMain 函数
    void ServiceMain(int argc, char** argv) 
    { 
        int error; 
     
     
        ServiceStatus.dwServiceType = SERVICE_WIN32; 
        ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 
        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
        ServiceStatus.dwWin32ExitCode = 0; 
        ServiceStatus.dwServiceSpecificExitCode = 0; 
        ServiceStatus.dwCheckPoint = 0; 
        ServiceStatus.dwWaitHint = 0; 
     
        hStatus = RegisterServiceCtrlHandler("MemoryStatus", (LPHANDLER_FUNCTION)ControlHandler); 
        if (hStatus == (SERVICE_STATUS_HANDLE)0) 
        { 
            // Registering Control Handler failed
            return; 
        } 
        // Initialize Service 
        error = InitService(); 
        if (!error) 
        {
            // Initialization failed
            ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
            ServiceStatus.dwWin32ExitCode = -1; 
            SetServiceStatus(hStatus, &ServiceStatus); 
            return; 
        } 
        // We report the running status to SCM. 
        ServiceStatus.dwCurrentState = SERVICE_RUNNING; 
        SetServiceStatus (hStatus, &ServiceStatus);
              
        MEMORYSTATUS memory;
        // The worker loop of a service
        while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
        {//while循环内放置需要添加的代码
            char buffer[16];
            GlobalMemoryStatus(&memory);
            sprintf(buffer, "%d", memory.dwAvailPhys);
            int result = WriteToLog(buffer);
            if (result)
            {
                ServiceStatus.dwCurrentState = 
                    SERVICE_STOPPED; 
                ServiceStatus.dwWin32ExitCode = -1; 
                SetServiceStatus(hStatus, 
                    &ServiceStatus);
                return;
            }
            Sleep(SLEEP_TIME);
        }
        return; 
    }
    该函数是服务的入口点。它运行在一个单独的线程当中,这个线程是由控制分派器创建的。ServiceMain 应该尽可能早早为服务注册控制处理器。这要通过调用 RegisterServiceCtrlHadler 函数来实现。你要将两个参数传递给此函数:服务名和指向 ControlHandlerfunction 的指针。 
    它指示控制分派器调用 ControlHandler 函数处理 SCM 控制请求。注册完控制处理器之后,获得状态句柄(hStatus)。通过调用 SetServiceStatus 函数,用 hStatus 向 SCM 报告服务的状态。 
    dwServiceType:指示服务类型,创建 Win32 服务。赋值 SERVICE_WIN32 ; 
    dwCurrentState :指定服务的当前状态。因为服务的初始化在这里没有完成,所以这里的状态为SERVICE_START_PENDING ; 
    dwControlsAccepted :这个域通知 SCM 服务接受哪个域。本文例子是允许 STOP 和 SHUTDOWN 请求。处理控制请求将在第三步讨论; 
    dwWin32ExitCode 和 dwServiceSpecificExitCode :这两个域在你终止服务并报告退出细节时很有用。初始化服务时并不退出,因此,它们的值为 0 ; 
    dwCheckPoint 和 dwWaitHint :这两个域表示初始化某个服务进程时要30 秒以上。本文例子服务的初始化过程很短,所以这两个域的值都为 0 。
    调用 SetServiceStatus 函数向 SCM 报告服务的状态时。要提供 hStatus 句柄和 ServiceStatus 结构。注意ServiceStatus 一个全局变量,所以你可以跨多个函数使用它。ServiceMain 函数中,你给结构的几个域赋值,它们在服务运行的整个过程中都保持不变,比如:dwServiceType 。 
    在报告了服务状态之后,你可以调用 InitService 函数来完成初始化。这个函数只是添加一个说明性字符串到日志文件。如下面代码所示: 
    // 服务初始化 
    int InitService() 
    { 
        int result;
        result = WriteToLog("Monitoring started.");
        return(result); 
    }
    在 ServiceMain 中,检查 InitService 函数的返回值。如果初始化有错(因为有可能写日志文件失败),则将服务状态置为终止并退出 ServiceMain :
    error = InitService(); 
    if (error) 
    {
        // 初始化失败,终止服务 
        ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
        ServiceStatus.dwWin32ExitCode = -1; 
        SetServiceStatus(hStatus, &ServiceStatus); 
        // 退出 ServiceMain
        return; 
    }
    如果初始化成功,则向 SCM 报告状态:
    // 向 SCM 报告运行状态 
    ServiceStatus.dwCurrentState = SERVICE_RUNNING; 
    SetServiceStatus (hStatus, &ServiceStatus);
    接着,启动工作循环。每五秒钟查询一个可用物理内存并将结果写入日志文件。
    循环一直到服务的状态为 SERVICE_RUNNING 或日志文件写入出错为止。状态可能在ControlHandler 函数响应 SCM 控制请求时修改。
    # 第三步:    处理控制请求 
    在第二步中,你用 ServiceMain 函数注册了控制处理器函数。控制处理器与处理各种 Windows 消息的窗口回调函数非常类似。它检查 SCM 发送了什么请求并采取相应行动。 
    每次你调用 SetServiceStatus 函数的时候,必须指定服务接收 STOP 和 SHUTDOWN 请求。 
    STOP 请求是 SCM 终止服务的时候发送的。例如,如果用户在“ 服务” 控制面板中手动终止服务。SHUTDOWN 请求是关闭机器时,由 SCM 发送给所有运行中服务的请求。两种情况的处理方式相同: 
    写日志文件,监视停止; 
    向 SCM 报告 SERVICE_STOPPED 状态;
    由于 ServiceStatus 结构对于整个程序而言为全局量,ServiceStatus 中的工作循环在当前状态改变或服务终止后停止。其它的控制请求如:PAUSE 和 CONTINUE 在本文的例子没有处理。 
    控制处理器函数必须报告服务状态,即便 SCM 每次发送控制请求的时候状态保持相同。因此,不管响应什么请求,都要调用 SetServiceStatus 。 
    整个程序:
    // services.cpp : 定义控制台应用程序的入口点。
     
    #include "stdafx.h"
    #include <Windows.h> //调用 Win32 函数
    #include <stdio.h>//磁盘文件写入
     
    #define SLEEP_TIME 5000
    #define LOGFILE "D://project//c++//services//Debug//memstatus.txt" //exe文件所在文件夹
    
    
    SERVICE_STATUS ServiceStatus; 
    SERVICE_STATUS_HANDLE hStatus; 
    void ServiceMain(int argc, char** argv); 
    void ControlHandler(DWORD request); 
    int InitService();
    int WriteToLog(char* str);
     
    int _tmain(int argc, _TCHAR* argv[])
    {
        SERVICE_TABLE_ENTRY ServiceTable[2];
        ServiceTable[0].lpServiceName = "MemoryStatus";
        ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
     
     
        ServiceTable[1].lpServiceName = NULL;
        ServiceTable[1].lpServiceProc = NULL;
     
        StartServiceCtrlDispatcher(ServiceTable); 
    }
    
    
    void ServiceMain(int argc, char** argv) 
    { 
        int error; 
     
     
        ServiceStatus.dwServiceType = SERVICE_WIN32; 
        ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 
        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
        ServiceStatus.dwWin32ExitCode = 0; 
        ServiceStatus.dwServiceSpecificExitCode = 0; 
        ServiceStatus.dwCheckPoint = 0; 
        ServiceStatus.dwWaitHint = 0; 
     
        hStatus = RegisterServiceCtrlHandler("MemoryStatus", (LPHANDLER_FUNCTION)ControlHandler); 
        if (hStatus == (SERVICE_STATUS_HANDLE)0) 
        { 
            // Registering Control Handler failed
            return; 
        } 
        // Initialize Service 
        error = InitService(); 
        if (!error) 
        {
            // Initialization failed
            ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
            ServiceStatus.dwWin32ExitCode = -1; 
            SetServiceStatus(hStatus, &ServiceStatus); 
            return; 
        } 
        // We report the running status to SCM. 
        ServiceStatus.dwCurrentState = SERVICE_RUNNING; 
        SetServiceStatus (hStatus, &ServiceStatus);
     
        MEMORYSTATUS memory;
        // The worker loop of a service
        while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
        {
            char buffer[16];
            GlobalMemoryStatus(&memory);
            sprintf(buffer, "%d", memory.dwAvailPhys);
            int result = WriteToLog(buffer);
            if (result)
            {
                ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
                ServiceStatus.dwWin32ExitCode = -1; 
                SetServiceStatus(hStatus, &ServiceStatus);
                return;
            }
            Sleep(SLEEP_TIME);
        }
        return; 
    }
    
    
    void ControlHandler(DWORD request) 
    { 
        switch(request) 
        { 
        case SERVICE_CONTROL_STOP: 
            WriteToLog("Monitoring stopped.");
            ServiceStatus.dwWin32ExitCode = 0; 
            ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
            SetServiceStatus (hStatus, &ServiceStatus);
            return; 
     
     
        case SERVICE_CONTROL_SHUTDOWN: 
            WriteToLog("Monitoring stopped.");
            ServiceStatus.dwWin32ExitCode = 0; 
            ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
            SetServiceStatus (hStatus, &ServiceStatus);
            return; 
     
        default:
            break;
        } 
     
        // Report current status
        SetServiceStatus (hStatus, &ServiceStatus);
        return; 
    }
    
    
    int WriteToLog(char* str)
    {
        FILE* log;
        log = fopen(LOGFILE, "a+");
        if (log == NULL)
            return -1;
        fprintf(log, "%s ", str);
        fclose(log);
        return 0;
    }
    
    
    int InitService()
    {
        WriteToLog("Monitoring started.");
        return true;
    }
    # 第四步:    安装配置服务
    1. 开始-> 运行->cmd->回车
    2. 输入 sc create test binPath= 编译成功后的可执行文件的路径回车就创建了一个服务(注意:=”后面是必须空一格的,否则会出现错误)(test时创建的服务名,可自定义)
    3. 启动服务:net start 服务名称
    4. 暂停服务:net stop 服务名称
    5. 删除服务:sc delete 服务名称
     
    # 附录:本地执行情况
    makefile
    MemoryStatus : MemoryStatus.c
        gcc -o $@ $^
    
    clean : 
        rm -rf MemoryStatus.exe
    执行相关命令
     
    说明:要以admin身份执行
    sc create memoryStatus binPath= d:/git/cv/windows-service/MemoryStatus.exe
    net start memoryStatus
    cat /c/memstatus.txt
    net stop memoryStatus
    sc delete memoryStatus
  • 相关阅读:
    AM8 自定义表情包的实现方法
    Create STKNetDiskC Instance Error
    怎样实现文件发文功能
    企业云盘的数据备份
    一种可行的文档协同编辑方法实现
    【OI】二分图最大匹配
    【OI】位运算操作
    【OI】关于快速幂的简单理解
    【OI】线性筛
    【OI】指针线段树&指针
  • 原文地址:https://www.cnblogs.com/Netsharp/p/12856191.html
Copyright © 2011-2022 走看看