zoukankan      html  css  js  c++  java
  • Windows 服务程序(一)

    Windows 服务程序简介:

    Windows服务应用程序是一种需要长期运行的应用程序,它对于服务器环境特别适合。
    它没有用户界面,并且也不会产生任何可视输出。任何用户消息都会被写进Windows事件日志。
    因为这些特性,所以特别适用于后台程序。
    计算机启动时,服务会自动开始运行。它们不要用户一定登录才运行,它们能在包括这个系统内的任何用户环境下运行。
    通过服务控制管理器,Windows服务允许用户创建可在其自身的 Windows 会话中长时间运行的可执行应用程序。
    这些服务可在计算机启动时自动启动,可以暂停和重启,并且不显示任何用户界面。
     Windows服务应用程序通常可以在本地和通过网络为用户提供一些功能:
    例如客户端/服务器应用程序、Web 服务器、数据库服务器以及其他基于服务器的应用程序。
    Windows 服务程序如下图右方:
     
     
    编写 Windows 服务程序:
    Windows服务程序有着固定的模式,它一般由四个部分组成:main(), ServiceMain(), ServiceHandle(), MyWork()。
    main() 仅负责创建服务分派表并启动控制分派机制,典型代码如下:
    int main(void)
    {
        SERVICE_TABLE_ENTRYA ServTable[2];    // 创建服务分派表。
        ServTable[0].lpServiceName = "Test";  // 服务名称。类似于 Win32 应用程序的窗口名称。每个服务代表一项功能。
        ServTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;  //服务入口函数。
        ServTable[1].lpServiceName = NULL;  // 每一个服务分派表代表一个服务,分派表的最后一项必须是服务名和服务主函数域的 NULL 指针。
        ServTable[1].lpServiceProc = NULL;   // 我们称之为“哨兵”(所有值都为NULL),表示该服务表末尾。
        StartServiceCtrlDispatcher(ServTable);  // 启动控制分派机制。
    return 0; }

    SERVICE_TABLE_ENTRYA 结构体:

    typedef struct _SERVICE_TABLE_ENTRYA {
      LPSTR                    lpServiceName;   // 服务名称。
      LPSERVICE_MAIN_FUNCTIONA lpServiceProc;   // 服务入口函数。
    } SERVICE_TABLE_ENTRYA, *LPSERVICE_TABLE_ENTRYA;

    StartServiceCtrlDispatcher() 介绍 :

    功能:连接程序主线程到服务控制管理程序。

    BOOL StartServiceCtrlDispatcher(
      CONST SERVICE_TABLE_ENTRYA *lpServiceStartTable // SERVICE_TABLE_ENTRYA 结构体变量。
    );                     

    返回值:非零表示成功,零表示失败。

    服务控制管理器 (SCM:Services Control Manager) 是一个管理系统所有服务的进程。

    当 SCM 启动某个服务时,它等待某个进程的主线程来调用 StartServiceCtrlDispatcher 函数,

    将分派表传递给 StartServiceCtrlDispatcher。

    这将把调用进程的主线程转换为控制分派器。该分派器启动一个新线程,

    该线程运行分派表中每个服务的 ServiceMain 函数分派器还监视程序中所有服务的执行情况。

    然后分派器将控制请求从 SCM 传给服务。

    分派表中所有的服务执行完之后,或者发生错误时。StartServiceCtrlDispatcher 调用返回。然后主进程终止。

    SCM 开始启动一个服务程序时,总是等待这个服务程序去调用 StartServiceCtrlDispatcher()。

    而当服务开始运行时,main() 将会调用 ServiceMain(), 直到 ServiceMain() 执行完毕或发生错误而退出,

    StartServiceCtrlDispatcher() 返回,主线程将会终止。

    ServiceMain() 服务入口函数,它的作用就是:将你需要执行的任务放到该函数中循环执行即可。这就是服务程序的工作函数。

    在ServiceMain执行你的任务前,需要给SERVICE_TABLE_ENTRY 分派表结构体进行赋值。

    典型代码如下:

    // SERVICE_STATUS ServiceStatus;  //这段代码应声明为全局变量,因多处使用。
    // SERVICE_STATUS_HANDLE hStatus; //这段代码应声明为全局变量,因多处使用。
    void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv) { // 初始化状态设置。 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_START_PENDING; // 即服务目前状态为 正在初始化。 ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
    // 这个通知 SCM 服务接收哪个域。本例包含 停止,暂停和继续,关机等命令。 ServiceStatus.dwWin32ExitCode
    = 0; // 这个域在终止服务并报告细节时很有用。下面这四个值一般不用关心,通常设置为 0。 ServiceStatus.dwCheckPoint = 0; // 用来报告它当前的事件进展情况的。 ServiceStatus.dwServiceSpecificExitCode = 0; // 这个域在终止服务并报告细节时也很有用。 ServiceStatus.dwWaitHint = 0; // 根据初始化过程的长短而定。 // 注册控制函数。 hStatus = RegisterServiceCtrlHandler("ServiceName", // 注册服务控制程序,注册成功后将会返回一个服务状态句柄。 (LPHANDLER_FUNCTION)ServiceHandler); // ServiceHandle 是服务控制程序。类似与 Windows 应用程序的窗口过程。 if (!hStatus)
    {
    printf("Register Service Error! ");
    system("pause");
    return;
    }
    SetServiceStatus(hStatus, &ServiceStatus);  // 设置服务状态。通过调用 SetServiceStatus 函数,向 SCM 报告服务的状态。
    if (GetLastError != NO_ERROR) // 返回调用线程最近的错误代码值,检查是否出错。 { // 如果出错,将其重设为停止状态。 ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;
    SetServiceStatus(hStatus, &ServiceStatus);
    printf("Start Error! ");
    system("pause");
    return; }
    // 没有错误就继续运行。
        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;
        SetServiceStatus(hStatus, &ServiceStatus);
        // 创建线程执行特定功能
        HANDLE hThread = CreateThread(NULL, 0, MyWork, NULL, 0, NULL); // MyWOrk 是特定功能的函数,如后门程序。
        if(hThread = NULL)
             return; }

    SERVICE_STATUS 结构体:

    typedef struct _SERVICE_STATUS {
      DWORD dwServiceType;   // 服务类型。
      DWORD dwCurrentState;   // 服务的当前状态。
      DWORD dwControlsAccepted;  // 服务在其处理函数中接受和处理的控制代码。
      DWORD dwWin32ExitCode;    // 服务用于报告启动或停止时发生错误的错误代码。
      DWORD dwServiceSpecificExitCode;  // 服务在启动或停止时发生错误时返回的错误代码。
      DWORD dwCheckPoint;   // 服务在长时间启动、停止、暂停或继续操作期间定期递增以报告其进度的检查点值。
      DWORD dwWaitHint;    // 将要进行 开始、停止、暂停或继续服务 操作所需的估计时间 (以毫秒为单位)。
    } SERVICE_STATUS, *LPSERVICE_STATUS;

    dwServiceType 的值:(组合)

    含义
    SERVICE_FILE_SYSTEM_DRIVER 该服务是一个文件系统驱动程序
    SERVICE_KERNEL_DRIVER 该服务是一个设备驱动程序
    SERVICE_WIN32_OWN_PROCESS 服务在自己的进程中运行(通常使用这个)
    SERVICE_USER_OWN_PROCESS 该服务在登录用户帐户下在其自己的进程中运行

    dwCurrentState 的值:

    含义
    SERVICE_CONTINUE_PENDING 服务处于从暂停状态恢复的过程中
    SERVICE_PAUSE_PENDING 服务正在暂停过程中,但还有没完全进入暂停状态
    SERVICE_PAUSED 服务已经暂停了
    SERVICE_RUNNING 服务正在运行了
    SERVICE_START_PENDING 服务在启动过程中,但还没有准备好对请求进行响应
    SERVICE_STOP_PENDING 服务正在停止过程中,但还没有完全进入停止状态
    SERVICE_STOPPED  服务已经停止了

    dwControlsAccepted 的值: (组合)

    含义
    SERVICE_ACCEPT_NETBINDCHANGE 该服务是一个网络组件,并且能够在服务在服务不重启的情况下,改变其所网络接收的绑定,接收SERVICE_CONTROL_NETBINDADD、SERVICE_CONTROL_NETBINDREMOVE、SERVICE_CONTROL_NETBINDENABLE、SERVICE_CONTROL_NETBINDDISABLE 的通知
    SERVICE_ACCEPT_PARAMCHANGE 服务在不重启的情况下能够重新读取其配置参数,接收 SERVICE_CONTROL_PARAMCHANGE 通知
    SERVICE_ACCEPT_PAUSE_CONTINUE 服务支持暂停和重启,服务能够接收到 SERVICE_CONTROL_PAUSE 和SERVICE_CONTROL_CONTINUE的通知
    SERVICE_ACCEPT_PRESHUTDOWN 系统在关闭前,能够收到系统的 SERVICE_CONTROL_PRESHUTDOWN 通知,用来处理一些关闭前的清理,xp之前不支持此控制码
    SERVICE_ACCEPT_SHUTDOWN 能够接收系统退出时的 SERVICE_CONTROL_SHUTDOWN 的通知,以便处理一些回收
    SERVICE_ACCEPT_STOP 能够接收 SERVICE_CONTROL_STOP 的通知来处理一些回收任务

    SetServiceStatus() 介绍:

    功能:更新服务控制管理器调用服务的状态信息。

    函数原型:BOOL SetServiceStatus(
                      SERVICE_STATUS_HANDLE hServiceStatus, //服务状态信息结构的句柄, 由 RegisterServiceCtrlHandler() 返回。
                      LPSERVICE_STATUS  lpServiceStatus   // 指向 SERVICE_STATUS 结构的指针包含呼叫服务的最新状态信息。
                      );

    返回值:非零表示成功,零表示失败。

    RegisterServiceCtrlHandler() 介绍:

    功能:注册一个函数以处理服务控制请求。

    函数原型:SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(
                      LPCSTR  lpServiceName, //调用线程运行的服务的名称, 即在 CreateService() 中指定的服务控制程序的服务名称。
                      LPHANDLER_FUNCTION lpHandlerProc   // 指向要注册的处理程序的函数。
                      );

    返回值:如果函数成功, 则返回值为服务状态句柄。如果函数失败, 返回值为零。

    CreateThread() 介绍:

    功能:该函数在 主线程 的基础上创建一个新线程。线程 终止运行后,

              线程对象仍然在系统中,必须通过 CloseHandle() 来关闭该线程对象。

    函数原型:HANDLE CreateThread(
                      LPSECURITY_ATTRIBUTES   lpThreadAttributes,
                      SIZE_T   dwStackSize,
                      LPTHREAD_START_ROUTINE  lpStartAddress,
                      LPVOID  lpParameter,
                      DWORD    dwCreationFlags,
                      LPDWORD  lpThreadId
                      );

    参数:

    lpThreadAttributes

    指向 SECURITY_ATTRIBUTES 结构的指针, 它确定返回的句柄是否可以由子进程继承。

    如果 lpThreadAttributes 为 NULL, 则无法继承句柄。

    dwStackSize

    堆栈的初始大小 (以字节为单位)。系统将此值舍入到最近的页面。

    如果此参数为零, 则新线程将使用可执行文件的默认大小。

    lpStartAddress

    指向要由线程执行的应用程序定义的函数。此指针表示线程的起始地址。

    lpParameter

    指向要传递给线程的变量的指针。

    dwCreationFlags

    控制线程创建的标志。通常情况下为 0。

    lpThreadId

    指向接收线程标识符的变量的指针。如果此参数为 NULL, 则不返回线程标识符。

    返回值:如果函数成功, 则返回值是新线程的句柄。如果函数失败, 返回值为 NULL。

    ServiceHandle() 服务控制函数,典型代码如下:

    void WINAPI ServiceHandler(DWORD fdwControl)
    {
        switch (fdwControl)
        {
        case SERVICE_CONTROL_PAUSE:
            ServiceStatus.dwCurrentState = SERVICE_PAUSED;
            break;
        case SERVICE_CONTROL_CONTINUE:
            ServiceStatus.dwCurrentState = SERVICE_RUNNING;
            break;
        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN:
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            ServiceStatus.dwCheckPoint = 0;
            ServiceStatus.dwWaitHint = 0;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
    default: break; } SetServiceStatus(hStatus, &ServiceStatus); // 重设服务状态。 return; }

     入门代码如下:(目前的代码仍然不能当做服务来运行,还需要 SCM 进行安装)

    #include<stdio.h>
    #include<Windows.h>
    SERVICE_STATUS ServiceStatus;
    SERVICE_STATUS_HANDLE hStatus;
    void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
    void WINAPI ServiceHandler(DWORD fdwControl);
    DWORD WINAPI MyWork(LPVOID lpParam);
    int main(void) { SERVICE_TABLE_ENTRY ServTable[2]; ServTable[0].lpServiceName = (LPSTR)"Test"; ServTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; ServTable[1].lpServiceName = NULL; ServTable[1].lpServiceProc = NULL; StartServiceCtrlDispatcher(ServTable); return 0; } void WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_START_PENDING; ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN; ServiceStatus.dwWin32ExitCode = 0; ServiceStatus.dwCheckPoint = 0; ServiceStatus.dwServiceSpecificExitCode = 0; ServiceStatus.dwWaitHint = 0; hStatus = RegisterServiceCtrlHandler("ServiceName", (LPHANDLER_FUNCTION)ServiceHandler); if (!hStatus)
        {
            printf("Register Service Error! ");
            system("pause");
            return;
         }

    SetServiceStatus(hStatus, &ServiceStatus);
    if (GetLastError() != NO_ERROR) { ServiceStatus.dwCurrentState = SERVICE_STOPPED; ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;
    SetServiceStatus(hStatus, &ServiceStatus);
    printf("Start Service Error! ");
    system("pause");
    return; }
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;
    SetServiceStatus(hStatus, &ServiceStatus);
    // 从这里开始可以放入你想服务为你所做的事情。
    HANDLE hThread = CreateThread(NULL, 0, MyWork, NULL, 0, NULL);
    if(hThread = NULL)
    return; } void WINAPI ServiceHandler(DWORD fdwControl) {
    switch (fdwControl) { case SERVICE_CONTROL_PAUSE: ServiceStatus.dwCurrentState = SERVICE_PAUSED; break; case SERVICE_CONTROL_CONTINUE: ServiceStatus.dwCurrentState = SERVICE_RUNNING; break; case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: ServiceStatus.dwCurrentState = SERVICE_STOPPED; ServiceStatus.dwCheckPoint = 0; ServiceStatus.dwWaitHint = 0; SetServiceStatus(hStatus, &ServiceStatus); return;
    default: break; } SetServiceStatus(hStatus, &ServiceStatus); return; }

    DWORD WINAPI MyWork(LPVOID lpParam)
    {
     return 0;
    }
  • 相关阅读:
    微信小程序使用场景及取名“潜”规则
    微信小程序入门——Mustache语法学习
    微信小程序开发需要注意的29个坑
    微信小程序入门——怎么建多个项目?(导入官方Demo程序进行学习)
    精进:如何成为一个很厉害的人---书摘
    软件项目如何控制需求蔓延
    给现有MVC 项目添加 WebAPI
    Web API与国际化
    基于Attribute的Web API路由设置
    NuGet学习笔记1——初识NuGet及快速安装使用
  • 原文地址:https://www.cnblogs.com/M-Anonymous/p/9380772.html
Copyright © 2011-2022 走看看