Service程序
2010年12月13日
用ATL非常简单,ATL会帮你生成一个框架。
以下是用SDK的:
编写NT的Service程序
任何OS都需要一个方法使当系统起动时就能运行一个或一些进程。在UNIX下那些程序由Daemons处理。如UNIX下的mail,cron,finger等等,在DOS下有TSR处理。在3.1下面,把程序放在startup组内也行。在NT下则由所谓Service来处理。在NT里当你启动后,至少有十五个service已经在运行了,其中最简单的莫过于Messenger Service了。当启动时,Messenger建立一个mailslot并且监视着它。当mailslot的队列里出现message时,它就会向NT报告。时间表
service稍微复杂些,当有人用at命令时,时间表service隔一会儿查一下任务表,如果时间到就执行。FTP服务器是最好的高级Service的例子。
在以下几种情况下应该考虑把程序写成service:
* 程序是用来控制某种硬件设备的。
* 程序需要不停地处理来访信息并返回的。(如BBS)
* 程序要从某个数据源周期性地拿数据。
* 一切有关RPC调用的程序。
理解NT service程序的原理
打开control panel里的Service可以看见诸如Clipbook Server, Computer Browser等已经在里面了。新写的Service也要被加进这个表。要被加进这个表,在注册表内要加到HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services下面。每个Service必须响应一些标准的事件:
* Start:开始Service程序
* Stop:停止service程序
* Pause:暂停
* Continue:继续暂停的service
所有的service程序被一个叫Service Control Manager(SCM)管理着。 但个的Service程序是一个EXE程序,但必须符合SCM的接口。否则这个service程序不会工作。
SCM的规定接口:
1) EXE程序必须有main或者WinMain函数。这个函数必须立即调用StartServiceCtrlDispatcher函数。调用后,EXE便被注册到SCM里并给SCM一个指向ServiceMain()的指针,当Service程序被start时就知道哪里去调用了。当然ServiceMain()的名字可以另起一个。但main函数注册完ServiceMain到SCM后就要return。正因为这是SCM管理调用的,所以一个Service程序在DOS命令行执行不了。
2) 在起动service时,SCM要调用ServiceMain函数。ServiceMain函数有几件事要负责。主要的一个就是立即调用RegisterServiceCtrlHandler函数,该函数则会去注册Handler函数。RegisterServiceHandler会返回一个Handle,当服务程序需要送消息给SCM时就通过这个Handle。ServiceMain也起动了一个线程,这个现程则做这个Service程序本来应该做的事。当这个线程启动后,ServiceMain其实就开始等待有事件发生。ServiceMain返回时也就是服务程序要终止的时候。当ServiceMain返回了,服务程序也终止了。
3)Handler函数有个switch来分别处理从SCM收到的控制请求。缺省情况下SCM发送以下常数:
* SERVICE_CONTROL_STOP:让服务程序停止。
* SERVICE_CONTROL_PAUSE:让服务程序暂停。
* SERVICE_CONTROL_CONTINUE:让服务程序继续。
* SERVICE_CONTROL_INTERROGATE:让服务程序立即报告状态。
* SERVICE_CONTROL_SHUTDOWN:让服务程序Shutdown。
它也可以发送自定义常数(数字在128至255之间)。
当建立一个程序,里面有Main,ServiceMain和Handler,有一个线程做服务时,就是个完整的服务程序了。
一个最简单的服务程序实例:
理论前三讲里都讲的差不多了,这里是个最小最简单的服务程序例子
这个服务程序的作用是每过两秒种喇叭鸣一下。你也可以调节鸣叫的间隔时间。这个例子虽小,但它响应每个SCM控制信号。正因为如此,我想这是个很好的例子。
编译后,在Tools目录下用Install程序安装该服务程序:
..\tools\install beepserv "Beep Service " c:\....\beepserv.exe (注意一定要打对路径名)
别忘了得用NT管理员的身份login才能安装服务程序。想要删除用:
..\tools\remove beepserv
这里的源程序提供Makefile和Beepserve.mak你可以自己从IDE环境中或命令行编译。源程序beepserv.cpp如下:
// beepserv.cpp
// V星系的朋友们,have fun!
#include
#include
#include
#include
// Global variables
// 服务程序的名字
char *SERVICE_NAME = "BeepService ";
// 让ServiceMain等一下再结束用的事件
HANDLE terminateEvent = NULL;
// 和SCM通讯用的Handle,由RegisterServiceCtrlHandler
// 建立
SERVICE_STATUS_HANDLE serviceStatusHandle;
// 鸣叫的时间间隔。单位毫秒。
int beepDelay = 2000;
// 记录当前状态的标志。
BOOL pauseService = FALSE;
BOOL runningService = FALSE;
// 干实际工作的工作线程。
HANDLE threadHandle = 0;
void ErrorHandler(char *s, DWORD err)
{
cout CreateThread(0, 0,
(LPTHREAD_START_ROUTINE) ServiceThread,
0, 0, &id);
if (threadHandle==0)
return FALSE;
else
{
runningService = TRUE;
return TRUE;
}
}
// 恢复中断的服务
VOID ResumeService()
{
pauseService=FALSE;
ResumeThread(threadHandle);
}
// 暂停服务
VOID PauseService()
{
pauseService = TRUE;
SuspendThread(threadHandle);
}
// 让ServiceMan结束也就终止了服务程序
VOID StopService()
{
runningService=FALSE;
SetEvent(terminateEvent);
}
// 用SetServiceStatus更新服务程序的状态
BOOL SendStatusToSCM (DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode,
DWORD dwCheckPoint,
DWORD dwWaitHint)
{
BOOL success;
SERVICE_STATUS serviceStatus;
// 填入SERVICE_STATUS
serviceStatus.dwServiceType =
SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = dwCurrentState;
if (dwCurrentState == SERVICE_START_PENDING)
serviceStatus.dwControlsAccepted = 0;
else
serviceStatus.dwControlsAccepted =
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN;
if (dwServiceSpecificExitCode == 0)
serviceStatus.dwWin32ExitCode =
dwWin32ExitCode;
else
serviceStatus.dwWin32ExitCode =
ERROR_SERVICE_SPECIFIC_ERROR;
serviceStatus.dwServiceSpecificExitCode =
dwServiceSpecificExitCode;
serviceStatus.dwCheckPoint = dwCheckPoint;
serviceStatus.dwWaitHint = dwWaitHint;
// 传送状态给SCM
success = SetServiceStatus (serviceStatusHandle,
&serviceStatus);
if (!success)
StopService();
return success;
}
// 把从service control manager受到的时间分而治之
VOID ServiceCtrlHandler (DWORD controlCode)
{
DWORD currentState = 0;
BOOL success;
switch(controlCode)
{
// 停止 the service
case SERVICE_CONTROL_STOP:
currentState = SERVICE_STOP_PENDING;
success = SendStatusToSCM(
SERVICE_STOP_PENDING,
NO_ERROR, 0, 1, 5000);
// Stop the service
StopService();
return;
// 暂停service
case SERVICE_CONTROL_PAUSE:
if (runningService && !pauseService)
{
success = SendStatusToSCM(
SERVICE_PAUSE_PENDING,
NO_ERROR, 0, 1, 1000);
PauseService();
currentState = SERVICE_PAUSED;
}
break;
// 恢复
case SERVICE_CONTROL_CONTINUE:
if (runningService && pauseService)
{
success = SendStatusToSCM(
SERVICE_CONTINUE_PENDING,
NO_ERROR, 0, 1, 1000);
ResumeService();
currentState = SERVICE_RUNNING;
}
break;
// 更新当前状态
case SERVICE_CONTROL_INTERROGATE:
// it will fall to bottom and send status
break;
// 终止时什么也不用做。你可以做些扫尾工作,但
// 记住必须非常快做完!
case SERVICE_CONTROL_SHUTDOWN:
return;
default:
break;
}
SendStatusToSCM(currentState, NO_ERROR,
0, 0, 0);
}
// 错误处理。
VOID terminate(DWORD error)
{
// 如果terminateEvent发生, 关闭它.
if (terminateEvent)
CloseHandle(terminateEvent);
// 发个message给scm
if (serviceStatusHandle)
SendStatusToSCM(SERVICE_STOPPED, error,
0, 0, 0);
// 如果线程在,杀了它
if (threadHandle)
CloseHandle(threadHandle);
}
// 开始服务程序时SCM会调用下面的serviceMain函数。
// ServiceMain一返回。服务程序就结束了。
VOID ServiceMain(DWORD argc, LPTSTR *argv)
{
BOOL success;
// 立即调用Registration函数
serviceStatusHandle =
RegisterServiceCtrlHandler(
SERVICE_NAME,
(LPHANDLER_FUNCTION) ServiceCtrlHandler);
if (!serviceStatusHandle)
{
terminate(GetLastError());
return;
}
success = SendStatusToSCM(
SERVICE_START_PENDING,
NO_ERROR, 0, 1, 5000);
if (!success)
{
terminate(GetLastError());
return;
}
// 制造终止事件。
terminateEvent = CreateEvent (0, TRUE, FALSE,
0);
if (!terminateEvent)
{
terminate(GetLastError());
return;
}
success = SendStatusToSCM(
SERVICE_START_PENDING,
NO_ERROR, 0, 2, 1000);
if (!success)
{
terminate(GetLastError());
return;
}
if (argc == 2)
{
int temp = atoi(argv[1]);
if (temp < 1000)
beepDelay = 2000;
else
beepDelay = temp;
}
success = SendStatusToSCM(
SERVICE_START_PENDING,
NO_ERROR, 0, 3, 5000);
if (!success)
{
terminate(GetLastError());
return;
}
// 开始服务程序。
success = InitService();
if (!success)
{
terminate(GetLastError());
return;
}
success = SendStatusToSCM(
SERVICE_RUNNING,
NO_ERROR, 0, 0, 0);
if (!success)
{
terminate(GetLastError());
return;
}
// 等待终止信号。
WaitForSingleObject (terminateEvent, INFINITE);
terminate(0);
}
VOID main(VOID)
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ SERVICE_NAME,
(LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL }
};
BOOL success;
// 向SCM注册。
success =
StartServiceCtrlDispatcher(serviceTable);
if (!success)
ErrorHandler( "In StartServiceCtrlDispatcher ",
GetLastError());
}
发表评论
-
JS/VBS配合Adodb.Stream处理字节数据/输出二进制文件
2012-01-20 02:13 1335JS/VBS配合Adodb.Stream处理 ... -
用Wscript的Arguments属性实现vbs的命令行参数形式启动
2012-01-20 02:13 1239用Wscript的Arguments属性实现vbs的命令行参数 ... -
VBS--自动定时截图工具
2012-01-20 02:13 1094VBS--自动定时截图工具 2010年09月01日 一、 ... -
使用 Iisftp.vbs 创建 FTP 站点
2012-01-20 02:13 678使用 Iisftp.vbs 创建 FTP ... -
CuteFTP上传脚本(VBS)
2012-01-20 02:13 644CuteFTP上传脚本(VBS) 2010年08月13日 ... -
调用规范与可变参数表
2012-01-19 10:03 582调用规范与可变参数表 ... -
编程基本
2012-01-19 10:03 471编程基本 2011年09月03日 繁 -
观察者模式
2012-01-19 10:03 577观察者模式 2011年12月19日 class JPDe ... -
12.29遛狗日记
2012-01-19 10:03 69512.29遛狗日记 2011年12月30日 01.18 ... -
如何导出wince6.0的SDK 添加MFC支持
2012-01-17 02:44 694如何导出wince6.0的SDK 添加MFC支持 2010年 ... -
Windows下NDK开发环境的搭建(Cygwin+Android-SDK+NDK)
2012-01-17 02:44 514Windows下NDK开发环境的搭 ... -
SDK详解
2012-01-17 02:44 562SDK详解 2010年11月01日 sdk SDK( ... -
sdk
2012-01-17 02:44 662sdk 2011年05月19日 SD ... -
微软计划今春推Kinect Windows SDK开发工具
2012-01-17 02:44 519微软计划今春推Kinect Windows SDK开发工具 ... -
RIA
2012-01-15 22:20 571RIA 2010年04月12日 Ja ... -
Java和flash通信中数据的zlib压缩与解压缩
2012-01-15 22:20 828Java和flash通信中数据的zlib压缩与解压缩 200 ... -
ArcGIS Server for Flex 资源收集
2012-01-15 22:20 542ArcGIS Server for Flex 资源 ... -
TWaver Flex会给电信软件开发带来变革吗?
2012-01-15 22:20 642TWaver Flex会给电信软件开发带来变革吗? 2010 ... -
天地会flash资源导航
2012-01-15 22:20 2025天地会flash资源导航 2011年02月25日 一 f ...
相关推荐
delphi 6/Kylik2 soap/Web Service程序设计篇下册+代码,非常好的教程。
《实战Delphi6/Kylix2/SOAP/Web Service程序设计篇》(李维著)
Delphi6 SOAP Web Service程序设计篇
实战Delphi6.Kylix2.SOAP.Web Service程序设计篇李维著.zip
实战Delphi6.Kylix2.SOAP.Web Service程序设计篇李维著 实战Delphi6.Kylix2.SOAP.Web Service程序设计篇李维著 实战Delphi6.Kylix2.SOAP.Web Service程序设计篇李维著
D6 SOAP_WEB SERVICE程序设计第二部分压缩包(共22.3M)
Delphi/Kylix SOAP/WEB SERVICE程序设计.part4
Visual C++源代码 102 如何异步调用Web Service程序Visual C++源代码 102 如何异步调用Web Service程序Visual C++源代码 102 如何异步调用Web Service程序Visual C++源代码 102 如何异步调用Web Service程序Visual ...
Delphi/Kylix SOAP/WEB SERVICE程序设计.part6
Delphi 教程 系列书籍 (039) 《Delphi6.Kylix2.SOAP.Web Service程序设计篇》 网友(邦)整理 EMail: shuaihj@163.com 【Delphi系列书籍下载】(辛苦整理,大家珍惜!!!) ...
《实战Delphi6.Kylix2.SOAP.Web Service程序设计篇》配书代码。
本资源通过bash script脚本,实现包装cygwin平台下的服务管理工具cygrunsrv,以方便使用 类似于ubuntu service命令的接口,来管理cygwin下的服务
C#发现之旅 C#开发Windows Service程序 该软件能监视指定目录下的文件和子目录的新增,修改,删除和重命名操作,并将操作日志记录到一个数据库中。 2. 该软件以Windows服务的形式运行,能监视不同的用户帐户的操作...
Web Services 是什么? Anywhere、Anytime、AnyDevice 能够在一个分布式的计算环境中动态地描述、发布、发现和调用服务 Web上的对象访问技术……… Why We Need Web Services? 软件变服务 整合...
gSOAP 开发 Web Service 程序
开发过程中遇到Windows Service程序,需本地调试是否正确,总结了一点小坑,留作己用
学习webservice的书籍 下册分3个压缩包
通过vc 中Windows application 工程创建Windows service程序