摘要: 何为服务?Windows 会话中可长时间运行的可执行应用程序。这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面。这种服务非常适合在服务器上使用,或任何时候,为了不影响在同一台计算机上工作的其他用户,需要长时间运行功能时使用。一个服务首先是一个Win32可执行程序,如果要写一个功能完备且强大的服务,需要熟悉动态连接库(DLL)、结构异常处理、内存映射文件、虚拟内存、设备I/O、线程及其同步、Unicode以及其他的由WinAPI函数提供的应用接口。


Windows Service概要

实现一个服务程序,主要需要完成两个部分:

  • 服务运行函数(相当于Thread中的run):服务运行函数,一般被呼为ServiceMain。但是它的名称与ThreadProc一样,其命名没有特殊要求,只是参数接口和调用类型必须与要求一致:
void WINAPI serviceImpl(DWORD argc, LPCWSTR* argv);
  • 服务控制函数:Windows中提供了强大的命令行工具sc.exe,用于对服务进行管理。可以实现对服务的所有控制,包括安装、删除、配置、启动、停止等。为此,我们需要在自己的服务程序中对这些命令做出响应。也就是需要实现一个服务控制函数,当接收到不同命令的时候,执行不同的操作,具体操作自行定义。

一个服务包括若干属性,包括服务名称、显示名称、服务类型、描述、可执行文件路径、启动类型、服务状态、启动参数、依存关系等。

  1. 服务名称:唯一标识一个服务;
  2. 显示名称:在系统服务管理界面和sc.exe中显示名称;
  3. 服务类型:包括SERVICE_FILE_SYSTEM_DRIVER(文件系统驱动服务)、SERVICE_KERNEL_DRIVER(驱动服务)——(2种)运行于内核态。
    SERVICE_WIN32_OWN_PROCESS(独立进程服务)、SERVICE_WIN32_SHARE_PROCESS(共享进程服务)——(2种)运行于用户态。

本文主要讲解Windows服务运行函数开发。Windows服务与多线程极为类似,服务运行函数一般称为ServiceMain,与线程中的ThreadProc(线程执行体)一样,其函数名并没有特殊的要求,只要求其参数接口和返回值调用类型与要求一致即可,这里所说的ServiceMain(服务运行函数)也可以理解为Thread中的run,虽然不是很恰当。服务程序通过调用StartServiceCtrlDispatcher API函数设置服务主函数。

其中,StartServiceCtrlDispatcher 函数原型如下:

StartServiceCtrlDispatcherW(_In_ CONST  SERVICE_TABLE_ENTRYW    *lpServiceStartTable);

以上函数中只有一个结构体,SERVICE_TABLE_ENTRYW的原型也给出如下:

typedef struct _SERVICE_TABLE_ENTRYW {LPWSTR                      lpServiceName;LPSERVICE_MAIN_FUNCTIONW    lpServiceProc;
}SERVICE_TABLE_ENTRYW, *LPSERVICE_TABLE_ENTRYW;

lpServiceName为服务名称;lpServiceProc为指向ServiceMain的函数指针。只要将函数的指针复制给lpServiceProc,再调用StartServiceCtrlDispatcher ,这个函数就称为了服务主函数。

没有服务控制函数,光有服务执行体——服务是跑不起来的。
服务控制函数可以用Windows提供的sc.exe 程序,在命令提示符的环境下,让服务运行体(ServiceMain)跑起来。这里使用另外一种方式,使用Windows API来控制服务执行体。所谓服务——其实也就是一个EXE可执行程序。

Service一般开发流程

一般服务开发流程:

  1. 先将服务运行体(ServiceMain)生成,得到一个XXX.exe。(本文讲解内容)
  2. 接着,将该exe放在电脑的某一个位置,再运行服务控制函数,服务就跑起来了。
  3. 这里需要注意一点,开发服务程序,需要以管理员方式运行Visual Studio 2013等版本。(或者在DOS下操作时,就是不编写服务控制程序,而采用Windows提供的sc.exe管理服务,也同样需要管理员权限。)

在服务程序开发过程中,有一个很重要的结构体SERVICE_STATUS 其具体定义如下:

typedef struct _SERVICE_STATUS {DWORD   dwServiceType;                   //可执行文件类型DWORD   dwCurrentState;                  //服务当前状态DWORD   dwControlsAccepted;              //指明服务接受何种控制DWORD   dwWin32ExitCode;                 //错误报告码(Win32已义错误)DWORD   dwServiceSpecificExitCode;       //自定义错误码DWORD   dwCheckPoint;                    //处于进程的哪一步DWORD   dwWaitHint;                      //超时时间
} SERVICE_STATUS, *LPSERVICE_STATUS;
  1. dwServiceType指明服务可执行文件的类型。如果你的可执行文件中只有一个单独的服务,就把这个成员设置成SERVICE_WIN32_OWN_PROCESS;如果拥有多个服务的话,就设置成SERVICE_WIN32_SHARE_PROCESS。除了这两个标志之外,如果你的服务需要和桌面发生交互(当然不推荐这样做),就要用“OR”运算符附加上SERVICE_INTERACTIVE_PROCESS。这个成员的值在你的服务的生存期内绝对不应该改变。
  2. dwCurrentState是这个结构中最重要的成员,它将告诉SCM你的服务的现行状态。为了报告服务仍在初始化,应该把这个成员设置成SERVICE_START_PENDING。在以后具体讲述CtrlHandler函数的时候具体解释其它可能的值。
  3. dwControlsAccepted指明服务愿意接受什么样的控制通知。如果你允许一个SCP去暂停/继续服务,就把它设成SERVICE_ACCEPT_PAUSE_CONTINUE。很多服务不支持暂停或继续,就必须自己决定在服务中它是否可用。如果你允许一个SCP去停止服务,就要设置它为SERVICE_ACCEPT_STOP。如果服务要在操作系统关闭的时候得到通知,设置它为SERVICE_ACCEPT_SHUTDOWN可以收到预期的结果。这些标志可以用“OR”运算符组合。
  4. dwWin32ExitCodedwServiceSpecificExitCode是允许服务报告错误的关键,如果希望服务去报告一个Win32错误代码(预定义在WinError.h中),它就设置dwWin32ExitCode为需要的代码。一个服务也可以报告它本身特有的、没有映射到一个预定义的Win32错误代码中的错误。为了这一点,要把dwWin32ExitCode设置为ERROR_SERVICE_SPECIFIC_ERROR,然后还要设置成员dwServiceSpecificExitCode为服务特有的错误代码。当服务运行正常,没有错误可以报告的时候,就设置成员dwWin32ExitCode为NO_ERROR。
  5. 最后的两个成员dwCheckPointdwWaitHint是一个服务用来报告它当前的事件进展情况的。当成员dwCurrentState被设置成SERVICE_START_PENDING的时候,应该把dwCheckPoint设成0,dwWaitHint设成一个经过多次尝试后确定比较合适的数,这样服务才能高效运行。一旦服务被完全初始化,就应该重新初始化SERVICE_STATUS结构的成员,更改dwCurrentState为SERVICE_RUNNING,然后把dwCheckPoint和dwWaitHint都改为0。
    • dwCheckPoint成员的存在对用户是有益的,它允许一个服务报告它处于进程的哪一步。每一次调用SetServiceStatus时,可以增加它到一个能指明服务已经执行到哪一步的数字,它可以帮助用户决定多长时间报告一次服务的进展情况。
    • 如果决定要报告服务的初始化进程的每一步,就应该设置dwWaitHint为你认为到达下一步所需的毫秒数,而不是服务完成它的进程所需的毫秒数。

Service 实战

对于Windows服务开发想了解更多可以阅读《Windows服务编写原理及探讨》一文,想实战开发可以参阅《Windows API—函数、接口、编程实例》一书。以下是一个完整的服务运行体(ServiceMain)实战。如果想直接讨论《Q群号:233688836》。在阅读本文的基础上,推荐阅读《Windows Service服务程序(1)实现对服务的控制和管理》,将对Windows服务编程有个更加深入的了解。

win32service.h

#ifndef _WIN32_SERVICE_H
#define _WIN32_SERVICE_H#include <Windows.h>extern SERVICE_STATUS g_ServiceStatus;
extern SERVICE_STATUS_HANDLE g_ServiceStatusHandle;void WINAPI serviceImpl(DWORD argc, LPCWSTR* argv);
void WINAPI serviceCtrlHandlerProc(DWORD opcode);#endif

win32service.cpp

#include "win32service.h"SERVICE_STATUS g_ServiceStatus;
SERVICE_STATUS_HANDLE g_ServiceStatusHandle;/*功能:服务控制函数*/
void WINAPI serviceCtrlHandlerProc(DWORD opcode){switch (opcode){case SERVICE_CONTROL_PAUSE:g_ServiceStatus.dwCurrentState = SERVICE_PAUSED; break;case SERVICE_CONTROL_CONTINUE:g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; break;case SERVICE_CONTROL_STOP:g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; break;case SERVICE_CONTROL_INTERROGATE:/*[ɪn'terə.ɡeɪt] v.审问;询问*/MessageBeep(MB_OK);default:break;}if (!SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus)){//set service status error.}
}
/*功能:服务运行函数*/
void WINAPI serviceImpl(DWORD argc, LPCWSTR* argv){g_ServiceStatus.dwServiceType = SERVICE_WIN32;g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;g_ServiceStatus.dwWin32ExitCode = 0;g_ServiceStatus.dwCheckPoint = 0;g_ServiceStatus.dwWaitHint = 0;g_ServiceStatusHandle = RegisterServiceCtrlHandlerA("demo_srv", serviceCtrlHandlerProc);if (g_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0){//register service ctrl handle error.return;}g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;if (!SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus)){//set service status error.return;}#if 0while (flag){//run service}
#endif
}

main.cpp

#include "win32service.h"int main(void){SERVICE_TABLE_ENTRYA DispatchTable[] = {{ "demo_srv", (LPSERVICE_MAIN_FUNCTIONA)serviceImpl },{ NULL, NULL }};if (!StartServiceCtrlDispatcherA(DispatchTable)){//start service ctrl dispather error.}return 0;
}

Windows Service服务程序的原理及实现(0)服务主函数 控制处理函数相关推荐

  1. python实现windows Service服务程序

    python实现windows Service服务程序 win32serviceutil.ServiceFramework是封装得很好的Windows服务框架,本文通过继承它来实现. 通过SvcDoR ...

  2. python写windows程序_【Python学习】Python 写Windows Service服务程序

    如下遇到自己编写的服务无法启动 需要添加环境变量(标红的) C:\Python27\Scripts;C:\Python27\;C:\Python27\chromedriver.exe;C:\Pytho ...

  3. python 编程服务_Python编写Windows Service服务程序

    如果你想用Python开发Windows程序,并让其开机启动等,就必须写成windows的服务程序Windows Service,用Python来做这个事情必须要借助第三方模块pywin32,自己去下 ...

  4. Windows Service

    Windows Service程序写好后,通过服务让它跑起来 服务启动后,可以调试本地程序,通过附加到进程,会看到 这是我们刚刚创建的服务,右击启动,在vs附加进程中会看到 点击附加,打上断点,就可以 ...

  5. zabbix mysql分开部署_Centos7 安装部署zabbix5.0服务端 超详细图文步骤

    一.系统环境准备 1.操作系统Centos7以上版本,注意尽量保证是空系统,没有安装数据库和Apache等中间件 2.安装前关闭selinux和firewall #ps:关闭防火墙并设置开机不启动(生 ...

  6. C# 编写Windows Service(windows服务程序)

    Windows Service简介: 一个Windows服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序.Windows服务程序虽然是可执行的,但是它不像一般的可执行文件通过双击就 ...

  7. mysql安装提示create_MySQL5.1安装时出现Cannot create windows service for mysql.error:0

    安装MySQL5.1过程中,我把以前MySQL5.0的GUI工具和服务器全部删掉,安装目录全部删掉,数据文件目录名字改掉,注册表用完美卸载清理了. 然后重启安装MySQL5.1(我以前遇到过服务启动不 ...

  8. mysql5.1安装失败_解决MySQL5.1安装时出现Cannot create windows service for mysql.error:0

    安装MySQL5.1过程中,我把以前MySQL5.0的GUI工具和服务器全部删掉,安装目录全部删掉,数据文件目录名字改掉,注册表用完美卸载清理了. 然后重启安装MySQL5.1(我以前遇到过服务启动不 ...

  9. C#写Windows Service(windows服务程序)

    背景:        要学习使用一个新东西,我们必须知道他是个什么东西.对于我们此次研究的windows服务来说,他又是个什么东西,其实也没有什么高深的了. windows service概述: 一个 ...

最新文章

  1. 如何在Ruby中写入文件?
  2. 三角形最小路径和—leetcode120
  3. 央视曝光紫砂锅名单_大家看今天中午《每周质量报告》没,美的牌 紫砂锅含有剧毒。家里有这牌子...
  4. 职场必须要会的餐桌礼仪
  5. 根据on,获取选中的元素
  6. 如何使用proteus仿真
  7. C11 多线程初学1
  8. 工程导论-----创造力
  9. 七夕情人节表白网页代码大全(浪漫的html表白源代码)
  10. Jeff Dean的神话
  11. 烟花绽放c语言程序设计摘要,描写烟花绽放的优美句子
  12. 前后端项目对接流程梳理
  13. 【历史上的今天】11 月 16 日:RISC-V 掌门人出生;微软发布 Windows CE;电子管问世
  14. C# TCP/IP通讯协议的整理(一)附带——基恩士扫码枪的使用
  15. 5-2 图书价格汇总
  16. jquery点击添加样式
  17. android 类对象的存储,android - 以共享首选项存储和检索类对象
  18. 英语日常口语对话(3)
  19. Day22 常用模块01
  20. mac上打开chm文件

热门文章

  1. linux磁盘配额实验11,Linux磁盘配额实验
  2. Javascript中Blob介绍
  3. 搜狐秋招java面经_活在秋招里|搜狐一站式面试分享
  4. python 之 多继承的顺序
  5. 争做IT新时代的弄潮儿
  6. java无法实例化类型_20.不能实例化类型变量
  7. 要想做好日语翻译,你需要这样做?
  8. 控制JSP页面上的文本框只能输入数字
  9. java -jar命令启动jar包
  10. OJ题之OOP身份证扩展(类构造与析构)