服务进程

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

用C语言编写Windows服务程序的五个步骤(附源代码)

Windows服务被设计用于需要在后台运行的应用程序以及实现没有用户交互的任务。为了学习这种控制台应用程序的基础知识,C(不是C++)是最佳选择。本文将建立并实现一个简单的服务程序,其功能是查询系统中可用物理内存数量,然后将结果写入一个文本文件。最后,你可以用所学知识编写自己的Windows服务。

当初我写第一个NT服务时,我到MSDN上找例子。在那里我找到了一篇Nigel Thompson写的文章:“Creating a Simple Win32Service in C++”,这篇文章附带一个C++例子。虽然这篇文章很好地解释了服务的开发过程,但是,我仍然感觉缺少我需要的重要信息。我想理解通过什么框架,调用什么函数,以及何时调用,但C++在这方面没有让我轻松多少。面向对象的方法固然方便,但由于用类对底层Win32函数调用进行了封装,它不利于学习服务程序的基本知识。这就是为什么我觉得C更加适合于编写初级服务程序或者实现简单后台任务的服务。在你对服务程序有了充分透彻的理解之后,用C++编写才能游刃有余。当我离开原来的工作岗位,不得不向另一个人转移我的知识的时候,利用我用C所写的例子就非常容易解释NT服务之所以然。

服务是一个运行在后台并实现勿需用户交互的任务的控制台程序。Windows NT/2000/XP操作系统提供为服务程序提供专门的支持。人们可以用服务控制面板来配置安装好的服务程序,也就是Windows 2000/XP控制面板|管理工具中的“服务”(或在“开始”|“运行”对话框中输入services.msc/s——译者注)。可以将服务配置成操作系统启动时自动启动,这样你就不必每次再重启系统后还要手动启动服务。

本文将首先解释如何创建一个定期查询可用物理内存并将结果写入某个文本文件的服务。然后指导你完成生成,安装和实现服务的整个过程。

第一步:主函数和全局定义

首先,包含所需的头文件。例子要调用Win32函数(windows.h)和磁盘文件写入(stdio.h):

#include

#include

接着,定义两个常量:

#define SLEEP_TIME5000

#define LOGFILE"C:\\MyServices\\memstatus.txt"

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

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

int WriteToLog(char*str)

{

FILE*log;

log=fopen(LOGFILE,"a+");

if(log==NULL)

return-1;

fprintf(log,"%s\n",str);

fclose(log);

return0;

}

声明几个全局变量,以便在程序的多个函数之间共享它们值。此外,做一个函数的前向定义:

SERVICE_STATUS ServiceStatus;

SERVICE_STATUS_HANDLE hStatus;

void ServiceMain(int argc,char**argv);

void ControlHandler(DWORD request);

int InitService();

现在,准备工作已经就绪,你可以开始编码了。服务程序控制台程序的一个子集。因此,开始你可以定义一个main函数,它是程序的入口点。对于服务程序来说,main的代码令人惊讶地简短,因为它只创建分派表并启动控制分派机。

void main()

{

SERVICE_TABLE_ENTRY ServiceTable[2];

ServiceTable[0].lpServiceName="MemoryStatus";

ServiceTable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain;

ServiceTable[1].lpServiceName=NULL;

ServiceTable[1].lpServiceProc=NULL;

//启动服务的控制分派机线程

StartServiceCtrlDispatcher(ServiceTable);

}

一个程序可能包含若干个服务。每一个服务都必须列于专门的分派表中(为此该程序定义了一个ServiceTable结构数组)。这个表中的每一项都要在SERVICE_TABLE_ENTRY结构之中。它有两个域:lpServiceName:指向表示服务名称字符串的指针;当定义了多个服务时,那么这个域必须指定;

pServiceProc:指向服务主函数的指针(服务入口点);

分派表的最后一项必须是服务名和服务主函数域的NULL指针,文本例子程序中只宿主一个服务,所以服务名的定义是可选的。

服务控制管理器(SCM:Services Control Manager)是一个管理系统所有服务的进程。当SCM启动某个服务时,它等待某个进程的主线程来调用StartServiceCtrlDispatcher函数。将分派表传递给StartServiceCtrlDispatcher。这将把调用进程的主线程转换为控制分派器。该分派器启动一个新线程,该线程运行分派表中每个服务的ServiceMain函数(本文例子中只有一个服务)分派器还监视程序中所有服务的执行情况。然后分派器将控制请求从SCM传给服务。

注意:如果StartServiceCtrlDispatcher函数30秒没有被调用,便会报错,为了避免这种情况,我们必须在ServiceMain函数中(参见本文例子)或在非主函数的单独线程中初始化服务分派表。本文所描述的服务不需要防范这样的情况。

分派表中所有的服务执行完之后(例如,用户通过“服务”控制面板程序停止它们),或者发生错误时。StartServiceCtrlDispatcher调用返回。然后主进程终止。

第二步:ServiceMain函数

Listing1展示了ServiceMain的代码。该函数是服务的入口点。它运行在一个单独的线程当中,这个线程是由控制分派器创建的。ServiceMain应该尽可能早早为服务注册控制处理器。这要通过调用RegisterServiceCtrlHadler函数来实现。你要将两个参数传递给此函数:服务名和指向ControlHandlerfunction的指针。

它指示控制分派器调用ControlHandler函数处理SCM控制请求。注册完控制处理器之后,获得状态句柄(hStatus)。通过调用SetServiceStatus函数,用hStatus向SCM报告服务的状态。

相关文档
最新文档