用C编写Windows服务程序的五个步骤

用C编写Windows服务程序的五个步骤,第1张

用C编写Windows服务程序的五个步骤,第2张

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

当我写第一个NT服务时,我去MSDN找了一个例子。在那里我找到了Nigel Thompson写的一篇文章:“用C++创建简单的Win32服务”,文章附有一个c++示例。虽然这篇文章很好地解释了服务的开发过程,但我仍然感觉缺少我需要的重要信息。我想了解通过什么框架,调用什么函数,什么时候调用,但是C++在这方面并没有让我轻松多少。面向对象的方法虽然方便,但由于底层Win32函数调用是用类封装的,不利于学习服务程序的基础知识。这就是为什么我觉得C更适合写初级服务程序或者实现简单后台任务的服务。在你对服务程序有了全面透彻的理解之后,你就可以很容易地用C++来编写它了。当我离开原来的工作,必须把我的知识转移给另一个人时,用我用c写的例子来解释为什么提供NT服务是非常容易的。

服务是一个控制台程序,在后台运行,实现不需要用户交互的任务。Windows/2000/XP操作系统为服务程序提供特殊支持。人们可以使用服务控制面板来配置已安装的服务程序,即Windows 2000/XP控制面板|管理工具中的“服务”(或在开始|运行对话框中输入services.msc/s)。您可以将该服务配置为在操作系统启动时自动启动,这样您就不必在每次重新启动系统时手动启动该服务。

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

步骤1:主要功能和全局定义

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

#包括
#包括

接下来,定义两个常数:

# define SLEEP _ TIME 5000
# define log file " C:\ \ my services \ \ mem status . txt "

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

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

int write tolog(char * str)
{
FILE * log;
log = fopen(LOGFILE," a+");
if(log = = NULL)
return-1;
fprintf(log," %s\n ",str);
fclose(log);
返回0;
}

声明几个全局变量,以便在程序的多个函数中共享它们的值。此外,对函数进行正向定义:

SERVICE_STATUS服务状态;
SERVICE _ STATUS _ HANDLE hStatus;

void ServiceMain(int argc,char * * argv);
void ControlHandler(DWORD请求);
int init service();

现在,准备工作已经就绪,可以开始编码了。服务程序控制台程序的子集。所以,首先你可以定义一个main函数,它是程序的入口点。对于服务程序,main的代码出奇的短,因为它只创建调度表和启动控制调度程序。

void main()
{
SERVICE _ TABLE _ ENTRY SERVICE TABLE[2];
ServiceTable[0]。lpServiceName = " MemoryStatus
ServiceTable[0]。lpServiceProc =(LP service _ MAIN _ FUNCTION)service MAIN;

ServiceTable[1]。lpServiceName = NULL
ServiceTable[1]。lpServiceProc = NULL

//启动服务
StartservicectrlDispatcher(service table)的控制调度程序线程;
}

一个程序可能包含几个服务。每个服务必须列在一个特殊的调度表中(为此,程序定义了一个ServiceTable结构数组)。该表中的每个项目都必须在SERVICE_TABLE_ENTRY结构中。它有两个域:

指向代表lpServiceName的字符串的指针;当定义了多个服务时,则必须指定该域;
lpservicepro:指向服务主函数(服务入口点)的指针;

调度表的最后一项必须是服务名的空指针和服务的主函数字段。文本示例程序只托管一个服务,因此服务名的定义是可选的。

服务控制管理器(SCM:服务控制管理器)是一个管理系统所有服务的进程。当SCM启动一个服务时,它等待一个进程的主线程调用StartServiceCtrlDispatcher函数。将调度表传递给StartServiceCtrlDispatcher。这将把调用进程的主线程转换成控制调度程序。dispatcher启动一个新线程,该线程运行调度表中每个服务的ServiceMain函数(本例中只有一个服务)。调度程序还监控程序中所有服务的执行。然后,调度程序将控制请求从SCM发送到服务。

注意:如果30秒内没有调用StartServiceCtrlDispatcher函数,将会报告一个错误。为了避免这种情况,我们必须在ServiceMain函数中(参见本文中的示例)或者在不是Main函数的单独线程中初始化服务调度表。本文中描述的服务不需要防范这种情况。

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

步骤2: ServiceMain函数

清单1显示了ServiceMain的代码。这个函数是服务的入口点。它在一个单独的线程中运行,该线程由控制调度程序创建。ServiceMain应该尽早为服务注册控制处理器。这是通过调用ReGISterServiceCtrlHadler函数实现的。您必须向该函数传递两个参数:服务名和指向ControlHandlerfunction的指针。

它指示控制调度程序调用ControlHandler函数来处理SCM控制请求。注册控制处理器后,获取状态句柄(hStatus)。通过调用SetServiceStatus函数,服务的状态将通过hStatus报告给SCM。

清单1展示了如何指定服务特征及其当前状态来初始化ServiceStatus结构。ServiceStatus结构的每个域都有其用途:

DwServiceType:表示服务类型并创建Win32服务。SERVICE _ WIN32;

指定服务的dwCurrentState。因为这里服务的初始化没有完成,所以这里的状态是service _ start _ pending

DwControlsAccepted:该域告诉SCM服务接受哪个域。本文中的示例是允许S和SHUTDOWN请求。处理控制请求将在第三步中讨论;

DwWin32ExitCode和dwServiceSpecificExitCode:当您终止服务并报告退出细节时,这两个域非常有用。服务在初始化时不会退出;因此,它们的值是0;

DwCheckPoint和dwWaitHint:这两个字段表示初始化一个服务进程需要30多秒。本文中示例服务的初始化过程非常短,因此两个字段的值都是0。

当调用SetServiceStatus函数向SCM报告服务状态时。提供hStatus句柄和ServiceStatus结构。请注意,ServiceStatus是一个全局变量,因此可以在多个函数中使用它。在ServiceMain函数中,你给结构的几个字段赋值,它们在服务操作的整个过程中保持不变,比如dwServiceType。

报告服务状态后,可以调用InitService函数来完成初始化。这个函数只是在日志文件中添加一个描述性的字符串。如下面的代码所示:

//服务初始化
int init service()
{
int result;
result = WriteToLog("开始监视,");
return(结果);
}

在ServiceMain中,检查InitService函数的返回值。如果初始化出错(因为写入日志文件可能会失败),请将服务状态设置为终止并退出ServiceMain:

error = init service();
if(error)
{
/初始化失败,服务
service status . dwcurrentstate = service _ speeded终止;
service status . dwwin 32 exit code =-1;
SetServiceStatus(hStatus,& service status);
//退出service main
return;
}

如果初始化成功,向SCM报告状态:

//向SCM
service status . dwcurrentstate = service _ running报告运行状态;
SetServiceStatus (hStatus,& service status);

然后,开始工作循环。每五秒钟查询一次可用物理内存,并将结果写入日志文件。

如清单1所示,循环直到服务的状态为SERVICE_RUNNING或者日志文件被错误写入。当ControlHandler函数响应SCM控制请求时,可以修改状态。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » 用C编写Windows服务程序的五个步骤

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情