C编写服务程序详细步骤

1、简介

  Windows 服务被设计用于需要在后台运行的应用程序以及实现没有用户交互的任务。为了学习这种控制台应用程序的基础知识,C(不是C++)是最佳选择。C++面向对象的方法固然方便,但由于用类对底层 Win32 函数调用进行了封装,它不利于学习服务程序的基本知识。
  服务是一个运行在后台并实现勿需用户交互的任务的控制台程序。Windows NT/2000/XP 操作系统提供为服务程序提供专门的支持。

2、步骤

2.1 主函数和全局定义

  包含所需的头文件。要调用 Win32 函数(windows.h)和其他所需要的

2.2 定义2个常量

1 #define SLEEP_TIME 5000
2 #define LOGFILE "E:\\test.txt"

SLEEP_TIME 指定两次连续查询可用内存之间的毫秒间隔。在第二步中编写服务工作循环的时候要使用该常量。

LOGFILE 定义日志文件的路径,你将会用 WriteToLog 函数将内存查询的结果输出到该文件

Log 函数定义如下:

 1 int Log(char* str)
 2 {
 3     FILE* log;
 4     log = fopen(LOGFILE, "a+");
 5     if (log == NULL)
 6         return -1;
 7     fprintf(log, "%s\n", str);
 8     fclose(log);
 9     return 0;
10 }    

2.3 声明几个全局变量

  作用:便在程序的多个函数之间共享它们值,做一个函数的前向定义:

1 SERVICE_STATUS ServiceStatus;
2 SERVICE_STATUS_HANDLE hStatus;
3 void ServiceMain(int argc, char** argv);
4 void ControlHandler(DWORD request);
5 int InitService();

2.4 编码

服务程序是控制台程序的一个子集。因此需要定义1个main函数,是程序的入口点。对于服务程序来说,main 的代码很简短,因为它只创建分派表并启动控制分派机。

 1 int main(int argc, const char *argv[])
 2 {
 3     SERVICE_TABLE_ENTRY ServiceTable[2];
 4
 5     ServiceTable[0].lpServiceName = _T(SERVICE_NAME);
 6     ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)service_main;
 7
 8     ServiceTable[1].lpServiceName = NULL;
 9     ServiceTable[1].lpServiceProc = NULL;
10     // 启动服务的控制分派机线程
11     StartServiceCtrlDispatcher(ServiceTable);
12     return 0;
13 }

2.5 分派表的域

  一个程序可能包含若干个服务。每一个服务都必须列于专门的分派表中(为此该程序定义了一个 ServiceTable 结构数组)。这个表中的每一项都要在 SERVICE_TABLE_ENTRY 结构之中。它有两个域:

  lpServiceName: 指向表示服务名称字符串的指针;当定义了多个服务时,那么这个域必须指定;
  lpServiceProc: 指向服务主函数的指针(服务入口点);
分派表的最后一项必须是服务名和服务主函数域的 NULL 指针,文本例子程序中只宿主一个服务,所以服务名的定义是可选的。

2.6 服务控制管理器(SCM:Services Control Manager)

  是一个管理系统所有服务的进程。当 SCM 启动某个服务时,它等待某个进程的主线程来调用 StartServiceCtrlDispatcher 函数。将分派表传递给 StartServiceCtrlDispatcher。这将把调用进程的主线程转换为控制分派器。该分派器启动一个新线程,该线程运行分派表中每个服务的 ServiceMain 函数(本文例子中只有一个服务)分派器还监视程序中所有服务的执行情况。然后分派器将控制请求从 SCM 传给服务。
  注意:如果 StartServiceCtrlDispatcher 函数30秒没有被调用,便会报错,为了避免这种情况,我们必须在 ServiceMain 函数中(参见本文例子)或在非主函数的单独线程中初始化服务分派表。本文所描述的服务不需要防范这样的情况。分派表中所有的服务执行完之后(例如,用户通过“服务”控制面板程序停止它们),或者发生错误时。StartServiceCtrlDispatcher 调用返回。然后主进程终止。

2.7 ServiceMain 函数

1)ServiceStatus 结构的域用途

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 函数中,你给结构的几个域赋值,它们在服务运行的整个过程中都保持不变。

  报告了服务状态之后,可调用 InitService 函数来完成初始化。检查 InitService 函数的返回值。如果初始化有错(因为有可能写日志文件失败),则将服务状态置为终止并退出。

  初始化成功,则向 SCM 报告状态:
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    BOOL bstatus = SetServiceStatus(hStatus,&ServiceStatus);

  启动工作循环,执行所希望的工作。知道服务的状态为SERVICE_STOP或者SERVICE_SHUTDOWN或日志文件写入出错为止。状态可能在 ControlHandler 函数响应 SCM 控制请求时修改。

2.8 处理控制请求

  在 ServiceMain 函数注册了控制处理器函数。控制处理器与处理各种 Windows 消息的窗口回调函数非常类似。它检查 SCM 发送了什么请求并采取相应行动。
  STOP 请求是 SCM 终止服务的时候发送的。针对的是单个服务,如同在“服务”控制面板中手动终止服务。SHUTDOWN 请求是关闭机器时,由 SCM 发送给所有运行中服务的请求。
这两种情况的处理方式相同。此时:
  1)写日志文件,监视停止
  2)向SCM报告SERVICE_STOPPED 状态
由于 ServiceStatus 结构对于整个程序而言为全局量,ServiceStatus 中的工作循环在当前状态改变或服务终止后停止.控制处理器函数必须报告服务状态,即便 SCM 每次发送控制请求的时候状态保持相同。因此,不管响应什么请求,都要调用 SetServiceStatus。

2.9 安装和配置服务

  程序编写好之后编译为exe文件。为在机器上安装该服务,需要SC.EXE可执行文件,是 Win32 Platform SDK 中附带的一个工具。可以专门创建一个文件夹保存自己创建服务的exe文件。

  安装服务方法:sc create 服务名 binPath= exe可执行文件路径(指定服务名和二进制文件路径)

一般自己创建的服务的启动类型是手动,即根据需要启动服务,可右键打开属性页设置。删除服务方法:sc delete 服务名

2.10 测试服务

  若将txt日志文件设置为只读,执行net start test,会提示:test 服务正在启动 .test 服务已经启动成功。但是执行net stop test时提示:没有启动 test 服务。说明服务并没有启动。去掉只读属性,启动服务,在将文件设成只读。服务将停止执行,因为此时日志文件写入失败。

原文地址:https://www.cnblogs.com/Sheenagh/p/12219443.html

时间: 2024-10-09 20:06:44

C编写服务程序详细步骤的相关文章

【OpenHW12参赛手记】ZedBoard-自定义IP核实现+PS成功调用【详细步骤+流程介绍+源码】 转载

文章来源 图片无法复制,请看原文 http://www.eefocus.com/jefby1990/blog/13-03/291975_490bc.html [OpenHW12参赛手记]ZedBoard-自定义IP核实现+PS成功调用[详细步骤+流程介绍+源码] 2013-03-07 17:56:30 分享: (图片请点击查看原图) 软件环境:WIN7_64 + ISE 14.4 (system_edition) 硬件:Zedboard.USB-Cable线 搭建图: 经过前几天的学习,查看数据

PostgreSQL安装详细步骤(windows)

PostgreSQL安装:一.windows下安装过程安装介质:postgresql-9.1.3-1-windows.exe(46M),安装过程非常简单,过程如下:1.开始安装: 2.选择程序安装目录: 注:安装 PostgreSQL 的分区最好是 NTFS 格式的.PostgreSQL 首要任务是要保证数据的完整性,而 FAT 和 FAT32 文件系统不能提供这样的可靠性保障,而且 FAT 文件系统缺乏安全性保障,无法保证原始数据在未经授权的情况下被更改.此外,PostgreSQL 所使用的"

OSG入门即osgEarth建立一个地球的详细步骤

OSG入门即osgEarth建立一个地球的详细步骤 转:http://blog.csdn.net/xiaol_deng/article/details/9246291 最近在学习有关osg的知识,刚开始一头雾水,幸好在老师的指导下才茅塞顿开,同时又结合网上的多个资料总结 一下作为一个初学者入门的经验.希望广大初学者少走弯路! 开始osg编程之前要做osg的编译准备,这里作为初学者最好选择简单的方法先熟悉为主.我们先下载一个编译好的VS2010+OSGEarth,注意大家看到这里可能会有许多不解,

unity3d开发的android应用中增加AD系统的详细步骤

unity3d开发的android应用中增加AD系统的详细步骤 博客分类: Unity3d unity3d Unity3d已经支持android,怎样在程序里增加admob? 试了一下,确实能够,只是必需要把androidproject的包名改成unitypackage里的包名. 决定自己也做一个unity3d的Plugins,记录一下步骤: 经过自己測试wozik提供的Plugins中的AndroidManifest.xml.res,libs.bin是必须的,其它东西都能够去掉. 依照这个思路

CentOS 6.5下Redis安装详细步骤

CentOS 6.5下Redis安装详细步骤 Redis简介: Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发工作由VMware主持. redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set –有序集合)和hash(哈希类

Zabbix3.4安装详细步骤

Zabbix3.4安装的详细步骤 一.zabbix介绍 现在大多数公司都会用到监控软件,主流的监控软件就是Zabbix了,当然还会有Nagios等其他的软件: Zabbix简介: zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营;并提供柔软的通知机制以让系统管理员快速定位/解决存在的各种问题. zabbix 由2部分构成,zabbix server 与可选组件zabbix agent. zabb

Oracle11g安装详细步骤

Oracle11g安装详细步骤,详见附件↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

VS2012/13本地发布网站详细步骤(可带数据库)

VS发布网站详细步骤 要在本地(自己的额电脑上)发布网站,首先你必须要搭建一个IIS服务器(具体搭建方法可参考我的上一篇博客).下面是具体步骤: 1.打开你的VS2012网站项目,右键点击项目>菜单中 重新生成一下网站项目:再次点击右键>发布 2.弹出网站发布设置面板,点击<新建..>,创建新的发布配置文件(配置文件可以随便写自己命名): 3.点击下一步:在发布方法中选"文件系统",这样我们可以发布到自己指定的本机文件上. 4.选择网站要发布的物理地址(可以使C

在CentOS6.4中安装配置LAMP环境的详细步骤

原文:在CentOS6.4中安装配置LAMP环境的详细步骤 本文详细介绍了CentOS6.4系统中安装LAMP服务并对其进行配置的过程,即安装Apache+PHP+Mysql,参照了网上大神的设置,其他Linux发行系统可以参考~ 在本文中部分命令操作需要root权限,输入‘su -’命令后输入密码即可切换root身份. 一.修改设置对安装做准备 1. 防火墙设置 设置/etc/sysconfig/iptables文件允许80端口和3306端口.因为80端口是http协议所使用的端口,如果防火墙