Service程序
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());
}
猜你喜欢
转载自nvy327ny.iteye.com/blog/1358003
今日推荐
周排行