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
  • 相关阅读:
    POJ3693 Maximum repetition substring —— 后缀数组 重复次数最多的连续重复子串
    SPOJ
    POJ2774 Long Long Message —— 后缀数组 两字符串的最长公共子串
    POJ3261 Milk Patterns —— 后缀数组 出现k次且可重叠的最长子串
    POJ1743 Musical Theme —— 后缀数组 重复出现且不重叠的最长子串
    SPOJ
    AC自动机小结
    HDU3247 Resource Archiver —— AC自动机 + BFS最短路 + 状压DP
    POJ1625 Censored! —— AC自动机 + DP + 大数
    Herding
  • 原文地址:https://www.cnblogs.com/Netsharp/p/12856191.html
Copyright © 2011-2022 走看看