Windows Service服务程序的原理及实现(0)服务主函数 控制处理函数
摘要: 何为服务?Windows 会话中可长时间运行的可执行应用程序。这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面。这种服务非常适合在服务器上使用,或任何时候,为了不影响在同一台计算机上工作的其他用户,需要长时间运行功能时使用。一个服务首先是一个Win32可执行程序,如果要写一个功能完备且强大的服务,需要熟悉动态连接库(DLL)、结构异常处理、内存映射文件、虚拟内存、设备I/O、线程及其同步、Unicode以及其他的由WinAPI函数提供的应用接口。
Windows Service概要
实现一个服务程序,主要需要完成两个部分:
- 服务运行函数(相当于
Thread
中的run
):服务运行函数,一般被呼为ServiceMain
。但是它的名称与ThreadProc
一样,其命名没有特殊要求,只是参数接口和调用类型必须与要求一致:
void WINAPI serviceImpl(DWORD argc, LPCWSTR* argv);
- 服务控制函数:Windows中提供了强大的命令行工具
sc.exe
,用于对服务进行管理。可以实现对服务的所有控制,包括安装、删除、配置、启动、停止等。为此,我们需要在自己的服务程序中对这些命令做出响应。也就是需要实现一个服务控制函数,当接收到不同命令的时候,执行不同的操作,具体操作自行定义。
一个服务包括若干属性,包括服务名称、显示名称、服务类型、描述、可执行文件路径、启动类型、服务状态、启动参数、依存关系等。
- 服务名称:唯一标识一个服务;
- 显示名称:在系统服务管理界面和sc.exe中显示名称;
- 服务类型:包括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一般开发流程
一般服务开发流程:
- 先将服务运行体(ServiceMain)生成,得到一个
XXX.exe
。(本文讲解内容) - 接着,将该exe放在电脑的某一个位置,再运行服务控制函数,服务就跑起来了。
- 这里需要注意一点,开发服务程序,需要以管理员方式运行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;
dwServiceType
指明服务可执行文件的类型。如果你的可执行文件中只有一个单独的服务,就把这个成员设置成SERVICE_WIN32_OWN_PROCESS;如果拥有多个服务的话,就设置成SERVICE_WIN32_SHARE_PROCESS。除了这两个标志之外,如果你的服务需要和桌面发生交互(当然不推荐这样做),就要用“OR”运算符附加上SERVICE_INTERACTIVE_PROCESS。这个成员的值在你的服务的生存期内绝对不应该改变。dwCurrentState
是这个结构中最重要的成员,它将告诉SCM你的服务的现行状态。为了报告服务仍在初始化,应该把这个成员设置成SERVICE_START_PENDING。在以后具体讲述CtrlHandler函数的时候具体解释其它可能的值。dwControlsAccepted
指明服务愿意接受什么样的控制通知。如果你允许一个SCP去暂停/继续服务,就把它设成SERVICE_ACCEPT_PAUSE_CONTINUE。很多服务不支持暂停或继续,就必须自己决定在服务中它是否可用。如果你允许一个SCP去停止服务,就要设置它为SERVICE_ACCEPT_STOP。如果服务要在操作系统关闭的时候得到通知,设置它为SERVICE_ACCEPT_SHUTDOWN可以收到预期的结果。这些标志可以用“OR”运算符组合。dwWin32ExitCode
和dwServiceSpecificExitCode
是允许服务报告错误的关键,如果希望服务去报告一个Win32错误代码(预定义在WinError.h中),它就设置dwWin32ExitCode为需要的代码。一个服务也可以报告它本身特有的、没有映射到一个预定义的Win32错误代码中的错误。为了这一点,要把dwWin32ExitCode设置为ERROR_SERVICE_SPECIFIC_ERROR,然后还要设置成员dwServiceSpecificExitCode为服务特有的错误代码。当服务运行正常,没有错误可以报告的时候,就设置成员dwWin32ExitCode为NO_ERROR。- 最后的两个成员
dwCheckPoint
和dwWaitHint
是一个服务用来报告它当前的事件进展情况的。当成员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)服务主函数 控制处理函数相关推荐
- python实现windows Service服务程序
python实现windows Service服务程序 win32serviceutil.ServiceFramework是封装得很好的Windows服务框架,本文通过继承它来实现. 通过SvcDoR ...
- python写windows程序_【Python学习】Python 写Windows Service服务程序
如下遇到自己编写的服务无法启动 需要添加环境变量(标红的) C:\Python27\Scripts;C:\Python27\;C:\Python27\chromedriver.exe;C:\Pytho ...
- python 编程服务_Python编写Windows Service服务程序
如果你想用Python开发Windows程序,并让其开机启动等,就必须写成windows的服务程序Windows Service,用Python来做这个事情必须要借助第三方模块pywin32,自己去下 ...
- Windows Service
Windows Service程序写好后,通过服务让它跑起来 服务启动后,可以调试本地程序,通过附加到进程,会看到 这是我们刚刚创建的服务,右击启动,在vs附加进程中会看到 点击附加,打上断点,就可以 ...
- zabbix mysql分开部署_Centos7 安装部署zabbix5.0服务端 超详细图文步骤
一.系统环境准备 1.操作系统Centos7以上版本,注意尽量保证是空系统,没有安装数据库和Apache等中间件 2.安装前关闭selinux和firewall #ps:关闭防火墙并设置开机不启动(生 ...
- C# 编写Windows Service(windows服务程序)
Windows Service简介: 一个Windows服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序.Windows服务程序虽然是可执行的,但是它不像一般的可执行文件通过双击就 ...
- mysql安装提示create_MySQL5.1安装时出现Cannot create windows service for mysql.error:0
安装MySQL5.1过程中,我把以前MySQL5.0的GUI工具和服务器全部删掉,安装目录全部删掉,数据文件目录名字改掉,注册表用完美卸载清理了. 然后重启安装MySQL5.1(我以前遇到过服务启动不 ...
- mysql5.1安装失败_解决MySQL5.1安装时出现Cannot create windows service for mysql.error:0
安装MySQL5.1过程中,我把以前MySQL5.0的GUI工具和服务器全部删掉,安装目录全部删掉,数据文件目录名字改掉,注册表用完美卸载清理了. 然后重启安装MySQL5.1(我以前遇到过服务启动不 ...
- C#写Windows Service(windows服务程序)
背景: 要学习使用一个新东西,我们必须知道他是个什么东西.对于我们此次研究的windows服务来说,他又是个什么东西,其实也没有什么高深的了. windows service概述: 一个 ...
最新文章
- 如何在Ruby中写入文件?
- 三角形最小路径和—leetcode120
- 央视曝光紫砂锅名单_大家看今天中午《每周质量报告》没,美的牌 紫砂锅含有剧毒。家里有这牌子...
- 职场必须要会的餐桌礼仪
- 根据on,获取选中的元素
- 如何使用proteus仿真
- C11 多线程初学1
- 工程导论-----创造力
- 七夕情人节表白网页代码大全(浪漫的html表白源代码)
- Jeff Dean的神话
- 烟花绽放c语言程序设计摘要,描写烟花绽放的优美句子
- 前后端项目对接流程梳理
- 【历史上的今天】11 月 16 日:RISC-V 掌门人出生;微软发布 Windows CE;电子管问世
- C# TCP/IP通讯协议的整理(一)附带——基恩士扫码枪的使用
- 5-2 图书价格汇总
- jquery点击添加样式
- android 类对象的存储,android - 以共享首选项存储和检索类对象
- 英语日常口语对话(3)
- Day22 常用模块01
- mac上打开chm文件