`
nvy327ny
  • 浏览: 15352 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

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());
  }
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics