怎样写一个 NT 服务程序

来源:互联网 发布:淘宝浏览单app有哪些 编辑:程序博客网 时间:2024/05/23 01:16


每个操作系统都需要有在后台执行任务的方法,无论是谁正在使用这部机器,这些任务都可以继续运行,后台任务可以处理各种重要的服务,包括系统的或者用户的。例如,一个信使服务可以监控网络,并且在接收到另一台机子的信息时,可以显示一个对话框。一个发送和接收传真的应用需要在启动的时候运行,并且不断地监控负责传真的modem,看有没有传真进来。一个家庭的或者办公室的安全程序,用来控制一件检测设备时,它需要不时地查询传感器,并且在适当的时候响应它。所有这些任务都需要cpu时间来执行它们,不过由于它们需要的cpu时间很少,因此可以放在后台而不影响用户使用系统。


在ms-dos中,后台的任务是通过tsr(terminate and stay resident)程序来处理的。这些程序经由autoexec.bat文件开始。
   在unix中,后台任务是通过daemons来处理的。在每次启动unix的过程中,你都可以看到操作系统启动一些任务,例如定时的程序(cron)和finger的daemons,然后才可以让首个用户登录。在windows nt中,后台的任务被称为服务。服务可在每次nt启动的时候运行,并且不管是谁登陆,都会一直运行下去。


windows nt的服务都是通过一般的可执行程序实现的,不同的是,它遵循内部的一个特定协议来设计,以便它们能够与服务控制管理器(scm,service control manager)进行正确的交互。在这篇文章中,你将学习到如何在windows nt中创建和安装简单的win32服务。一旦你懂得了这个简单的服务,你要建立自己的服务也不难了,因为所有的服务,不论是如何地复杂,都必须包含有同样基本的scm接口代码。只要符合scm的要求,其实为服务设计的可执行文件和一般的程序并没有多少的区别。


无论是对于编程者或者系统管理员,了解nt的服务如何工作都是很重要的。编程者就不必说了,因为他们要创建自己的服务,而对于系统管理员,也是同样重要的。因为后台的任务可以是很危险的。ms-dos和macintosh系统都是一个病毒的温床,因为它们在安全性方面先天不足,它们都可以允许任何人或者程序在任何时间创建后台的任务。windows nt和unix系统是较安全的,因为只有系统管理员才可以为系统增加后台的任务,不过,如果系统管理员加入了一个破坏性的后台程序,就它就可以为所欲为了。因此系统管理员要了解windows nt服务的技巧和权限设置,就可以避免加入有潜在危险的后台任务。


基本的概念


服务有两种不同的形式。驱动器服务使用驱动器协议,让nt可以与特定的硬件进行通信。另一个是win32服务,通过一般的win32 api来实现后台任务。这篇文章的重点是谈win32服务,因为它们更为常见,而且创建起来也很容易。任何的nt编程者通过使用一般的nt sdk(或者visual c++),并且可以用管理员的身份访问一台nt机器,都可以实现和安装自己的win32服务。如果你想创建一些在windows nt启动时就运行的程序,并且要求它会在系统中一直运行,你就要使用win32的服务。


在nt中,服务通过控制面板进行管理。在控制面板中,你会发现有一个服务的图标,打开它你会看到所有win32服务的清单。在那里你可以开始、停止、暂停和继续某个服务。你按下其中的启动按钮后,就会出现一个对话框,你可以修改启动操作以及服务使用的默认帐号。一个服务可以在系统启动的时候自动运行,也可以被完全禁止。或者设置为手动执行。在手动的时候,用户还可以设置启动的参数。要对服务中的项目作修改的话,你需要以一个管理员或者超级用户的身份登录。


windows nt自带有一些预装的任务,用来处理诸如网络信使服务的操作或者使用“at”命令定时执行的操作,以及分布的rpc命名。在你创建自己的服务时,你必须执行一个独立的安装步骤,以将服务的信息插入到服务管理工具的列表中,这些信息包括有新服务的名字、执行文件的名字和启动的类型等,都会写入到注册表中,这样在机器下次启动的时候,scm就会得到新服务的相关信息


创建一个新的服务


执行服务的程序也是一个exe文件,不过它必须符合某些特定的规范,以便可以与scm进行正确的交互。微软很细致地设计了函数调用的流程,你必须遵循这些流程,否则你的服务就不能工作。具体的规定如下所列。你可以在win32编程者的参考指南中找到以下涉及的函数,这些资料在sdk的win32在线帮助或者visual c++都有:


.服务的代码必须要有一个一般的main或者winmain函数。这个函数应该会马上调用startservicecrtldispatcher函数。通过调用这个函数,你可以让scm得到servicemain函数的指针,这样在scm要启动该服务时,就可以调用它


.在scm要启动服务的时候,就会调用servicemain函数。例如,如果管理员在服务管理器中按下启动的按钮,scm就会在一个独立的线程中执行servicemain函数。


servicemain应该调用registerservicectrlhandler函数,这样可以注册一个handler函数,以便scm对服务进行控制。handler函数的名字可以是任意的,不过它会在handler下的文档中列出来。registerservicectrlhandler函数会返回一个句柄,在服务需要发送状态信息给scm时,可以通过该句柄进行。


.servicemain函数也必须启动做该服务实际工作的线程。在服务停止前,servicemain函数是不应该有返回的。当它返回的时候,服务已经停止了。


.handler函数包含了一个switch语句,用来分析由scm传送过来的请求。默认的情况,scm可以发送以下任何的的控制常数:


service_control_stop - 要服务停止
service_control_pause - 要服务暂停
service_control_continue - 要服务继续
service_control_interrogate - 要服务马上报告它的状态
service_control_shutdown - 告诉服务即将关机
也可以创建自定义的常数(值在128到255之间),并且通过scm发送给服务。


如果你创建的exe包括有以上提到的main、servicemain和handler函数,以及执行服务自身任务的线程函数,那么你的服务程序设计就完成了。以下的图总结了这些不同的函数和scm之间的交互:






****************图一***********************


在本文的最后还有几段程序的列表,其中列表一为我们展示了一个可能是最简单的服务。该服务只发出"嘟"的响声。默认的状态下,它每两秒响一次。你可以通过启动的参数来修改发声的间隔。这个服务挺完整,它可以正确响应scm传来的每个控制信号。因此,这个程序可作为你创建自己服务的一个很好的模板。


main函数通过调用startservicectrldispatcher来注册servicemain函数。注册的操作使用了一个service_table_entry结构的数组。在这个例子中,该程序只包含了一个服务,因此在表中只会有一个项目。不过,对于一个exe文件,可以创建几个任务,这样在表中就会有几项,以识别不同的servicemain函数。在调用startservicectrldispatcher之前,可在main函数中放入初始化的代码,不过这些代码必须在少于30秒内完成,否则,scm会认为某些地方出错而终止服务。


在自动或者手动启动服务时,将会调用servicemain函数。servicemain函数将包含有以下的步骤:
1.它马上调用registerservicectrlhandler来注册handler函数,作为scm控制该服务的handler函数
2.然后它将调用sendstatustoscm函数,将当前的进程通报给scm。第四个参数是一个“click count”值,在程序每次更新状态时,它的值就会增加。scm和其它的程序可以根据click count的值来知道初始化期间进行的处理。最后的参数是"wait hint",是用来告诉scm在click count下次更新前,它需要等待的时间(以毫秒计算)。


3.servicemain接着会创建一个事情,该事件在函数的底部使用,可让它一直运行,直到scm发出一个stop的请求。


4.接着,servicemain检查启动的参数。参数可在用户手动启动服务时,通过服务管理工具中的启动参数传送过来。这些参数以一个argv形式的数组进入servicemain函数。


5.如果你的服务需要处理其它初始化的任务,它们应该放在这一步,在调用initservice之前。
6.servicemain函数接着调用initservice函数,这将启动线程并做服务的真正工作。


如果该调用成功,svervicemain将会通知scm服务已经成功启动。


7.servicemain将调用waitforsingleobject,用来等待terminateevent事件对象被设置。这个对象通过handler函数设置,一旦它被设置,servicemain将调用终止的函数来做清除的工作,然后就返回并停止服务。


你从上面可以看到,在这个函数中并没有多少灵活的地方。除了第5步外,你必须按步执行以上提到的任务,否则服务将不可以正确启动。


终止函数清除所有打开的句柄,并且发送一个状态的信息给scm,告诉它服务现已停止。


在scm要暂停、继续、询问或者停止服务时,它就会调用handler函数。要停止服务,handler设置terminateevent,这样做会导致servicemain(它会以一个独立线程的形式执行)终止并且返回。一旦servicemain返回,服务就停止了。


sendstatustoscm 函数负责发送服务的当前状态给scm。


在需要启动服务线程时,servicemain就会调用initservice函数。该函数调用createthread来为服务创建一个新的线程。


servicethread函数包含有该服务真正要做的工作。在这个例子中,该线程包含有一个无限的循环,以一个预定义的时间间隔发出响声。在你创建自己的服务时,你可以在该线程中放入任何的代码,调用win32函数或者你自己的函数。


安装和移除服务


为了使用上面提到的发响声服务,你必须安装它。安装可让scm知道这个服务,并且让scm将它加入到控制面板中的服务列表中。表单二的代码展示了如何安装一个服务。


表单2先通过openscmanager函数打开一个到scm的连接。在openscmanager的调用中,你必须指定你要做的东西,以便scm可以验证这个行为。如果你登录的帐号没有足够的权限,该调用将返回null。






createservice的调用是真正用来装入新服务的。它使用openscmanager返回至scm的指针、名字、标签和命令行中的exe文件,还有一些标准的参数值。使用


service_win32_own_process表明该服务的exe文件只包含有一个服务,而


service_demand_start则表明该服务是手动而不是自动启动的。使用命令行安装程序的一个典型格式如下:






install beepservice "beeper" c:\winnt\beep.exe






第一个参数是scm内部使用的服务名字。这个名字也用在以后移除服务。第二个参数


是一个标识符,即服务管理中显示的名字。第三个参数指出该服务的执行文件的路径。在你安装服务后,可在控制面板的服务管理中启动它。如果有错,可在win32 api的在线文档中查出错误代码的含义。






要移除服务,你要按列表3的步骤进行。它首先打开一个到scm的连接,然后使用openservice函数打开一个与服务的连接。列表3中接着查询服务来看它是否现已停止了。如果不是的话,就会停止它。deleteservice 用来从控制面板中移除服务,移除操作的典型格式如下:


remove beepservice


需要的话,你也可以在马上重新安装服务。


结论


服务对于windows nt是很重要的一部分,因为它可让你对操作系统进行扩展。使用列表1的代码作为一个模板,你将会发现要建立一个自己的新服务是非常简单的。
0 0
原创粉丝点击