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());
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值