zoukankan      html  css  js  c++  java
  • Windows API-后台服务

    Windows API-后台服务

    C++创建Windows后台服务程序

    C++创建Windows后台服务程序

    服务程序通常编写成控制台类型的应用程序,一个遵守服务控制管理程序接口要求的程序包含下面三个函数:

    • 服务程序主函数(main):调用系统函数 StartServiceCtrlDispatcher 连接程序主线程到服务控制管理程序。
    • 服务入口点函数(ServiceMain):执行服务初始化任务,同时执行多个服务的服务进程有多个服务入口函数。
    • 控制服务处理程序函数(Handler):在服务程序收到控制请求时由控制分发线程引用。
    - 服务程序主函数
    #include <iostream>
    #include "Windows.h"
    
    #define SERVICE_NAME "srv_demo"
    SERVICE_STATUS ServiceStatus;
    SERVICE_STATUS_HANDLE hServiceStatusHandle;
    
    int main (int argc, const 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;
    }
    
    首先声明几个全局变量,以便在程序的多个函数之间共享它们值。之后在主函数中创建一个分派表。分派表是SERVICE_TABLE_ENTRY 类型结构,它有两个域:
    
    - lpServiceName: 指向表示服务名称字符串的指针;当定义了多个服务时,那么这个域必须指定
    - lpServiceProc: 指向服务主函数的指针(服务入口点)
    
    一个程序可能包含若干个服务。每一个服务都必须列于分派表中,分派表的最后一项的两个域都必须为 NULL 指针。并且在只有一个服务的情况下,服务名是可选的。
    
    StartServiceCtrlDispatcher函数启动服务的控制分派机线程,当分派表中所有的服务执行完之后(服务为停止状态),或发生运行时错误,该函数调用返回,进程终止。
    
    
    -服务入口点函数
    
    void WINAPI service_main(int argc, char** argv)
    {
        ServiceStatus.dwServiceType        = SERVICE_WIN32;
        ServiceStatus.dwCurrentState       = SERVICE_START_PENDING;
        ServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
        ServiceStatus.dwWin32ExitCode      = 0;
        ServiceStatus.dwServiceSpecificExitCode = 0;
        ServiceStatus.dwCheckPoint         = 0;
        ServiceStatus.dwWaitHint           = 0;
        hServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceHandler);
        if (hServiceStatusHandle==0)
        {
            DWORD nError = GetLastError();
        }
        //add your init code here
    
        //add your service thread here
    
    
        // Initialization complete - report running status
        ServiceStatus.dwCurrentState       = SERVICE_RUNNING;
        ServiceStatus.dwCheckPoint         = 0;
        ServiceStatus.dwWaitHint           = 9000;
        if(!SetServiceStatus(hServiceStatusHandle, &ServiceStatus))
        {
            DWORD nError = GetLastError();
        }
    
    }
    
    上面给出的是是服务的入口点函数示例代码。它运行在一个单独的线程当中,这个线程由控制分派器创建。该函数应尽快调用 RegisterServiceCtrlHadler 函数为服务注册控制处理器,注册完控制处理器之后,获得状态句柄(hServiceStatusHandle)。
    
    ServiceStatus 结构的每个域的用途如下:
    - dwServiceType :指示服务类型,创建 Win32 服务。赋值 SERVICE_WIN32
    - dwCurrentState :指定服务的当前状态。因为服务的初始化在这里没有完成,所以这里的状态为SERVICE_START_PENDING
    - dwControlsAccepted :这个域通知 SCM 服务接受哪个域。本文例子是允许 STOP 和 SHUTDOWN 请求。处理控制请求将在第三步讨论
    - dwWin32ExitCode 和 dwServiceSpecificExitCode :这两个域在你终止服务并报告退出细节时很有用。初始化服务时并不退出,因此,它们的值为 0
    - dwCheckPoint 和 dwWaitHint :这两个域表示初始化某个服务进程时要30 秒以上。本文例子服务的初始化过程很短,所以这两个域的值都为 0
    
    - SetServiceStatus 函数用于向 SCM 报告服务的状态。需要提供 hStatus 句柄和 ServiceStatus 结构。
    
    
    
    - 控制服务处理程序函数
    void WINAPI ServiceHandler(DWORD fdwControl)
    {
        switch(fdwControl)
        {
            case SERVICE_CONTROL_STOP:
            case SERVICE_CONTROL_SHUTDOWN:
            ServiceStatus.dwWin32ExitCode = 0;
            ServiceStatus.dwCurrentState  = SERVICE_STOPPED;
            ServiceStatus.dwCheckPoint    = 0;
            ServiceStatus.dwWaitHint      = 0;
    
            //add you quit code here
    
            break;
            default:
                return;
        };
        if (!SetServiceStatus(hServiceStatusHandle,  &ServiceStatus))
        {
            DWORD nError = GetLastError();
        }
    }
    在第二步中,我们用 RegisterServiceCtrlHadler函数注册了控制处理器函数。控制处理器与处理各种 Windows 消息的窗口回调函数非常类似。它检查 SCM 发送了什么请求并采取相应行动。
    
    STOP 请求是 SCM 终止服务的时候发送的。例如,如果用户在“ 服务” 控制面板中手动终止服务。SHUTDOWN 请求是关闭机器时,由 SCM 发送给所有运行中服务的请求。两种情况的处理方式相同。
    
    控制处理器函数必须报告服务状态,即便 SCM 每次发送控制请求的时候状态保持相同。因此,不管响应什么请求,都要调用 SetServiceStatus 。
    
    
    • 创建一个服务
      sc cteate ServiceName binPath= "D:server.exe"
      注意:该指令运行时需要管理员权限,且“=”后面必须空一格。
    • 启动服务
      sc start ServiceName 或 通过windows提供的控制界面来启动服务
      若服务启动时出现如下错误信息:“服务没有及时响应启动或控制请求”,这是因为运行作为服务的应用程序不是按服务的流程写的。
    • 停止服务
      sc stop ServiceName
    • 删除服务
      sc delete ServiceName

    main函数及service_main函数中均有argc、argv参数,可以向这两个函数中传递参数.

    • 向main函数中传递参数需要在创建服务时指定:
      sc create atest binPath= "D:Project FilesImosServerx64Release -port=1024"
    • 向service_main函数中传递参数需要在启动服务时指定
      sc start atest -port=1024
    • 测试代码段
    FILE* log = fopen("D:\log.txt", "a");
    for (int i = 0; i < argc; ++i)
    {
        fprintf(log, "main: %s
    ", argv[i]);
    }
    fclose(log);
    

    ————————————————
    版权声明:本文为CSDN博主「blade1080」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/blade1080/java/article/details/82015323

  • 相关阅读:
    生活有时候就是个戏本
    Android dp、dpi、px
    iOS10以后相机、相册等授权问题
    iOS圆角性能问题
    激荡10年,珍贵的毕业礼物
    Android API 指南
    Android 配置
    Android Error
    安卓 MIUI真机测试
    iOS 同一段文字显示不同颜色
  • 原文地址:https://www.cnblogs.com/yongchao/p/13270502.html
Copyright © 2011-2022 走看看