作者:Joerg Koenig
翻译:Yuan Wenmao
原文:A Class For Building An NT Service
曾经尝试写一个NT服务吗?
我最近的一个项目需要开发一个CORBA服务,将其运行成NT服务,由于下一个项目也是需要NT服务,我就想花点时间把他做得通用一点给更多的项目使用。成果就是这个CNTService类,通过CodeGurus与大家分享。
通过这个类可以很容易的创建NT服务,所有的困难部分都被这个类完成了,你只需派生出你自己的类并重写”Run()”和”Stop()”两纯虚函数就可以了。当然还有好几个虚函数可以重写。
这个类接受一组命令行参数。比如说你可以使用-d选项使其按控制台程序运行,这样调试起来就容易多了。
此外这个类还可以安装卸载服务,也是通过命令行参数控制(当然你也可以重定义它)。
CNTService支持UNICODE和非UNICODE两种编译方式。
建立一个服务
现在让我们亲眼看看如何创建一个自己的服务。
步骤如下(假设你使用的是VC++5.0)
1. 创建一个新的控制台应用程序工程文件。
2. 把NTService文件包放入新工程文件目录中。
3. 将 "NTService.cpp" 和"NTServiceEventLogMsg.mc" 添加到工程中。
4. 打开 Project->Settings ... 对话框并且如下图所示填写。
5. 编译"NTServiceEventLogMsg.mc"文件. 这样会产生 "NTServiceEventLogMsg.h" 和"NTServiceEventLogMsg.rc"两个文件。
6. 将产生的资源文件添加到工程中。
7. 从CNTService类派生出自己的服务类并且重写至少两个方法"Run()"和"Stop()"。
8. 写一个简单的"main()" 函数去启动你程序的功能(你可以使用 sample 工程作为一个好的起点)。
9. 一旦工程编译没有错误了,你就可以立即启动它。这之前你应该打再次打开 Project->Settings ... 对话框,选择 AllConfigurations ,选中工程名字那一行,激活 Debug 标签并在Program arguments:域增加 -d 选项。
现在你可以将你的服务当控制台程序运行并调试了,可以使用Ctrl-C或Ctrl-Break去停止服务(这会模拟一个对SCM(Service Control Manager)的停止请求)。
如何填写文件NTServiceEventLogMsg.mc的配置
现在让我们将服务开启为一个真正的NT服务(假定使用Sample工程)
1. 如果你的账号不属于管理员组,切换为管理员账户登入(这会是有些事情容易点)。
2. 使用 –i选项开启程序. 这样就安装了你的服务。
3. 如果最后一步顺利完成, 你就可以打开控制面板启动 Services程序。查找你的服务的显示名字并选中它(如果你尝试的是sample程序,则显示名字叫Very Simple Service )。
4. 按下 Startup ... 按钮,查看 LogOn As:项,现在可以勾选System Account。如果是sample程序,确保你勾选了 Interact With The Desktop项。这是必需的,因为sample程序使用了MessageBox()函数(这与桌面有关)。如果不勾选此项,sample 不仅不能正确运行还会自动挂起;如此你将不能再次停止服务!只要稍有经验便可增强sample程序,使它在安装此服务的同时便选中此选项,但我想保证sample 程序尽可能的简单,所以这个问题留给你实现。
Sample工程实现了一个非常简单的服务。派生类(声明和实现)和main()函数都包含在同一个文件当中(main.cpp)。这个文件少于100行(去除注释可以确保一页纸就能打印!)
服务只是每10秒弹出一个消息框。
更多信息,参看 CNTService.h- sample工程。我相信这是很好的注释(如果有意见,告知我)。
改变的修订版
· 命令行参数中增加两个切换选项: -e 使运行中的服务停止(类中的相应方法为:virtual BOOL EndService())和-s 如果服务未运行,使服务开启(对应方法:virtual BOOL StartupService())。
· Todd C. Wilson 增加了对Win95的支持。这使得你可以创建一个像Windows 95程序的服务(所谓的"Faceless Application",无界面应用程序)。就想服务一样, 这样的应用程序会跟随系统自动启动且一个用户注销后仍然存在。 我的经历对此类应用程序作用不大:我可以注销, 在我再次登入前系统是挂起的。 而且 BoundsChecker 5 (是的,我经常使用这个强大的工具测试我的程序)说, 下列语句返回一个无效指针(准确的说应该是“返回的指针未指向一个函数”)
typedef DWORD (WINAPI *fp_RegServProc)(DWORDdwProcessId,DWORD dwType);
fp_RegServProc fncptr=NULL;
// ...
HMODULE hModule =::GetModuleHandle(TEXT("kernel32.dll"));
fncptr=(fp_RegServProc)::GetProcAddress(hModule,"RegisterServiceProcess");
if (fncptr!=NULL)
(*fncptr)(0, RSP_SIMPLE_SERVICE);
如果达到最后一句,BoundsChecker 警告说, 指针fncptr 未指向一个函数。然而,sample继续执行未碰到严重的错误。
如果你打算开发一个windows95下的无界面应用程序,就需要注意这个问题。 Sample工程提供了配置项"Win32 Win95 Debug",你可以用来尝试此新特性。
NTService 使用 MFC
微软推荐将NT服务写成控制台应用程序。但是,也有可能将使用MFC写NT服务。 如此,并不需要创建一个MFC向导程序(当然也可以这样做,不过需要移除一些代码,因为通常你不需要文档&视图结构),一个简单的Win32程序就够了。确保你选择了工程中Settings对话框General标签中的Using MFC in a shared DLL项。此外,你还需选择C/C++标签中的Multithreaded DLL / DebugMultithreaded DLL, Code Generation 域的 Use run-time Library.
如果你使用了预编译头文件,确保不要定义 #define VC_EXTRALEAN,因为基于MFC的服务需要partsof the "rarely used stuff",这个宏会拒绝windows头文件 。
我增加了一个MFC示例新工程MFCService 作为sample服务工作区的一部分。尝试将它作为一个更复杂服务的起点。服务(安装、删除等)操作和上一章所讲一样。
CNTService包含三个文件:
NTService.h
NTService.cpp
NTServiceEventLogMsg.mc
Download Source 14KB
Download Sample Project 23KB
注意:NTServiceEventLogMsg.mc 文件为Telic Software International B.V.版权所有。
感谢Telic提供
最后更新时间: February 5, 1999