一、 NT 服务程序
所谓 NT 服务,实际上就是一个可以在系统启动时自动在一定身份下启动的伴随系统长时间存在的进程。象 FTP server 、 HTTP server 、脱机打印等都是采用 NT 服务的形式提供的。这实际上类似 Unix 的 root daemon 进程。 NT 服务归纳起来, NT 服务又以下几个特征:
1 、可以自启动,不需要交互启动。这对于服务器来说是一个重要的特征。当然,你可以决定服务是否自启动,甚至可以屏蔽某个服务。
2 、 NT 服务没有用户界面,基本上类似一个 DOS 程序,因为 NT 服务必须长时间运行,所以不想普通 win32 进程一样有自己的界面。但是 NT 服务可以同用户有界面交互,这是一类特殊的服务进程。可以通过 NT 的任务管理器来看到服务进程。
3 、 NT 服务通过 SCM ( Services Control Manager )接口来管理,安装、启动、停止、撤除等都需要 SCM 的接口功能来进行。控制面板的服务控制器就是利用 SCM 接口来管理系统中的所有服务的。实际上,还有一些可以控制服务的程序或者命令,有 net.exe 、服务器管理器等 、 SCM.exe 等。
4 、这些进程都以一定的身份运行,以方便进行服务器资源的存取。一般情况下使用域中的 LocalSystem 账号运行,此账号对本机上的大多数资源(除非特别禁止)有完全的存取权限,这样可以保证服务程序的“强大”。但是,也有些服务采用特别的账号运行,你也可以特别设定一个服务的帐号。
5 、由系统自动以线程方式运行,一般情况下不过多占用系统资源,这同普通的进程有所区别,如果不采用线程方式,一般进程往往消耗整个 CPU 资源。一般需要时时存在,又不能过多消耗资源的任务以服务来实现最合适。
二、服务控件
一般认为编写 NT 服务需要使用 C/C++ 来实现, VC6 利用 ATL 向导来提供一个基本的服务框架。具体实现步骤为 :File à New… à ATL COM AppWizard à service à Finish. 但是使用 VC 编写 NT 服务需要编写太多的代码,这也意味着需要太多的调试、维护。实际上, NT 服务不是必须由 C/C++ 才可以编写的,实际上可以由任何能够实现上一节几个特点的任何语言实现,包括 VB 。
VB 编写服务有那些好处呢,至少可以列出以下几条:
1 、编码简单,熟悉 Vb 语法的任何人理解本文后都可编写。
2 、意味着修改服务实现的逻辑简单,维护简单。
3 、可以利用几乎大多数的 Vb 中的组件功能,编写一个强大的服务,譬如 ado 等,如果用 VC 来实现,相信任何人都会发怵。
4 、 ( 牵强一点 ) 可以证明 Vb 在 Bill 的天空下是多么强大。
那么, Vb 如何实现 NT 服务编写呢?据我所至,至少有两种途径:
1、 按照 C/C++ 的思路利用 WinAPI 来实现。
2、 利用组件按照 Vb 传统方式实现。
如果利用方法 1 实际上是照搬 C/C++ 的套路,如果有更好的路子可以实现,相信任何人都不会走这条“绝路”,因为相对于其他语言来说这种编程完全丧失了 Vb 自身得特点同时也没有获得其他语言的任何优势。在这里,想告诉大家的是利用 OCX 来实现一个服务。如果您在 MSDN 中搜索 Samples\msdn\techart\4920\ ,您可以看到一个已经编写好的 vc5 的工程文件。编译这个工程实际上会得到一个 ntsvc.ocx 的。如果您对 C/C++ 不熟悉,可以从 http://www.mywebtech.net/download/ntsvc.zip 下载一个 ntsvc.ocx, 此 OCX 是我从 backoffice 碟中获得的,将其拷贝到 \winnt\system32\ 下,利用 regsvr32 ntsvc.ocx 命令注册之。这样,您的 Vb 就可以从 project\components… 引出的对话框列表中看到名为“ Microsoft NT Service Control ”项。此组件拥有我们创建一个服务的基本的功能 , 如果要编写一个 NT 服务,我们将其拖进我们的窗体,然后设定其属性,调用其与系统、注册表、 SCM 交互的功能就可以实现完成一个服务了。
我们首先了解这个组件的属性,并向大家解释这些属性的用法:
Account String , 账号属性,即本 NT 服务在哪一个 NT 域账号下运行,缺省是 LocalSystem 账号,实际上大多数的 NT 服务都可以在此账号下安全圆满的运行。
ControlsAccepted Long, 此服务接受那些 SCM 控制,为以下值:
0 允许 Start 以及 Stop .
2 允许 Pause 以及 Continue .
4 允许 shutdown 。
其他值,用户自定义的某些事件 .
利用这个属性,您可以自己决定 NT 服务进程某个(譬如某个不可中断操作)时刻是否允许 SCM 停止、暂停、启动等操作。
Dependencies String , 如果您编写的服务依赖于某个或者某些服务才能正常运行,您必须在注册服务时指定依赖的服务列表。 Dependencies 按照依赖顺序以 chr(0) 来分隔多个服务,最后必须以两个 chr(0) 结束。(大家可以看到这是一个 C/C++ 的存在痕迹)
DisplayName String, 显示名, NT 服务以何种名字显示给察看者。
Interactive Boolean , 是否允许有同桌面用户有交互的部分。
LoadOrderGroup String, 同 Dependencies 相关,决定如果本服务启动之前,那些服务必须启动,格式也类似,也以 chr(0) 分割,连续的两个 chr(0) 结尾。
Password String, 服务启动的口令,如果使用缺省得账号,就没有必要设定服务启动的帐号。
ServiceName String, 服务名,如果使用 net.exe 来控制服务, net.exe 的指定那一个服务的参数就是此属性中的字符串。
StartMode 枚举型,具体为:
vcStartAutomatic 2 服务可以自己启动
svcStartManual 3 服务手动启动
svcStartDisabled 4 服务不能自启动
另外有一个 Debug 属性,不做讨论。
我们要将一个 VB 程序当作一个 NT 服务,必须向系统作一些“申请” , 而相应的工作 VB 是无法很好的完成的。所以, NTSVC.ocx 提供了相应的方法留作我们想系统传递相关信息。
Install , 将当前 Vb 程序安装成 NT 服务,在此之前,您必须至少设置 DisplayName, ServiceName, ControlsAccepted 以及 StartMode 等属性。除此之外您可能还要设置 Account 、 Password 、 LoadOrderGroup 、 Dependencies 等。这些信息的设置正确与否,决定您的服务程序能否正常启动运行。
Uninstall, 将当前 NTSVC.ocx 指定的服务从系统注册表中删除。 NT 服务取决于系统服务注册表的设定,这是一个众所周知的秘密。
StartService ,将指定的服务启动,如果该服务注册了。
StopService ,停止服务,如果服务正在运行。
LogEvent , 记录服务事件。服务运行中,可能发生错误以及意料不到的事件,这些可以通过此方法记录下来,供管理员通过“事件察看器”察看相关的信息,以最优化服务。此方法有三个参数 event, id, message. Event 指发生的事件类型,可以设为以下值:
svcEventError 1 错误事件
svcEventWarning 2 警告事件 .
svcEventInformation 4 提供参考信息 .
svcEventAuditSuccess 8 审计成功 .
svcEventAuditFailure 10 审计失败
除了以上方法,可能用户还需要读写注册表,此控件还提供了注册表的访问方法:
DeleteSetting (section[, key])
GetAllSettings(section)
GetSetting(section, key[, default])
SaveSetting(section, key, setting).
三、编写服务
了解以上内容,下面我们开始来设计一个服务,通过例子,让大家理解如何在 VB 中编写服务 .
在此之前,我们决定写一个什么样的服务。我参考 C++Build 中的一个例子,写一个不断报警的服务进程。该进程启动后在后台不断间隔 5 秒发出 Beep 叫,这可以让大家更深切知道此服务的存在,虽然有些令人讨厌。服务的名字为 VBBeepSVC, 在 SCM 中显示为 The VB NT SVC View 。
跟着我一起来吧!
1 、创建工程,设定相关使用到的控件。
所有的 Vb 的控件必须有一个 Form 作为载体,所以,首先我们创建一个标准工程 , 选择菜单 project—>Components…, 然后选取( Microsoft NT Service Control ) , 会在 Toolbar 中出现 NT 服务控件。再拖一个 Timer 控件到 Form 上。然后保存一下。基本上,创建过程完成。
2 、设定控件属性。
选中 NtSvc.ocx 实例,在属性栏中设定: DisplayName: The VB NT SVC View,ServiceName: VBBeepSVC,StartMode:3( 手动启动服务 ). 其他的就缺省吧。
由于我们希望每个 5 秒就 beep 一次,所以我们必须依靠一种定时机制来实现,所以我们将 timer 的 Interval 设定位 5000 毫秒。
以上属性的设定视您的需要而定,我只是说在我的 VBBeepSVC 中如此设定足够了。
3 、编写代码,实现服务逻辑以及服务安装、撤除。
因为服务程序实际上是一个 Exe 文件,并且需要自己解决安装、撤除问题,因此需要在此程序中加入利用 NT 服务控件来实现安装、撤除问题。那么,应当在什么时候进行了。 VB 程序启动时正时 Form 装载的时候,所以,我们需要在窗体的 Load 事件中加入一些代码:
On Error GoTo Err_Load ‘ 如果出现错误就纪录以供参考
Dim strDisplayName As String
strDisplayName = NTService1.DisplayName
If Command = "-install" Then ‘ 当启动时带上 –install 的参数时
NTService1.Interactive = True
If NTService1.Install Then
Call NTService1.SaveSetting("Parameters", "TimerInterval", "1000") ‘ 系统参数存储
MsgBox strDisplayName & " 安装成功! "
Else
MsgBox strDisplayName & " 安装失败 "
End If
End ‘ 终止安装
Else
If Command = "-uninstall" Then ‘ 如果启动时带上 撤除参数
If NTService1.Uninstall Then
MsgBox strDisplayName & " 撤除成功 "
Else
MsgBox strDisplayName & " 撤除失败 "
End If
End ‘ 终止撤除
Else
End If
End If
‘ 假若不是安装或撤除操作,即为启动服务
Timer1.Interval = CInt(NTService1.GetSetting("Parameters", "TimerInterval", "2000"))
‘ 使用 Timer 控件来模拟服务的线程特性
NTService1.ControlsAccepted = svcCtrlPauseContinue ‘ 接受暂停、停止操作,意味着需要为此编码
NTService1.StartService ‘ 设置好参数后启动服务
Err_Load:
Call NTService1.LogEvent(svcMessageError, svcEventError, "[" & Err.Number & "] " & Err.Description) ‘svcMessageError 为 NT 服务控件的错误值
4 、添加控制服务的代码。
尽管服务的连续线程等特性是依赖 Timer 实现的,但是服务的控制却是有 SCM 接口向每一个服务发出的,表现在 VB 服务程序中为 NT <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-