服务控制管理器(SCM: Service Control Manager)

http://www.cnblogs.com/janmson/

 

Windows服务是其实一种特殊的二进制可执行文件,后缀名一般为EXE,之所以说它特殊,因为它具有同Windows   NT/2K系统的服务控制管理器(SCM:   Service   Control   Manager)通信。  
          服务控制管理器通过维护数据库对已经安装到系统的所有服务和驱动程序进行统一而安全的控制和管理。服务控制管理器是一个远程进程调用(RPC)服务器,在系统导入时自动启动。  
          一个简单的服务程序至少包括一些几个部分:  
        1.   Win32/控制台应用主程序;  
        2.   一个服务主程序,作为服务的导入点;  
        3.   一个服务控制处理器,就是同服务控制管理器SCM通信的函数;  
        4.   一个服务安装/反安装程序用于将一个EXE文件注册为一个服务。  
        下面我们针对上述几个部分分别介绍怎样构造一个Windows服务。  
   
  控制台应用主程序  
   
  在Win32下为WinMain函数,在控制台下为main函数,是服务的主程序。下面是服务主程序中至少要包含的语句。  
  #include   "Winsvc.h"                                                 //服务头文件  
  main()  
  {  
          ......  
          SERVICE_TABLE_ENTRY   Table[]={{"gkeyService",gkeyServiceMain},{NULL,NULL}};      
          StartServiceCtrlDispatcher(Table);    
                  ......  
  }  
  当然这是一个非常简单的主程序了。这里main只做了一件事情,就是填写SERVICE_TABLE_ENTRY结构数组Table。Table[0][0]是服务的名字(可以是您喜欢的任意字符串,此处我用的是gkeyService);Table[0][1]指定了服务主程序的名字,实际上这是一个指向服务主程序的函数指针,它也可以用您喜欢的函数名字(我用的是gkeyServiceMain)。现在通过调用参数为SERVICE_TABLE_ENTRY结构数组的函数StartServiceCtrlDispatcher()开始启动服务解析。注意这个函数的参数必须要符合一定的格式,Table[1][0]和Table[1][1]必须是NULL,就是说到了数组的结尾。当然并非必须这样,如果需要在这个执行程序中运行多个服务,可以在这个数组列表中加入更多的入口,构成多对服务名称和服务中程序,自然您需要在以下的步骤中需要为每个服务构造相应的完成函数。  
   
  服务主程序  
  典型的服务主程序的声明如下:  
  void   WINAPI   gkeyServiceMain(   DWORD   argc,   LPTSTR   *argv   )  
   
          在gkeyServiceMain函数中,需要实现的主要步骤包括:    
  1.   用合适的值填写SERVICE_STATUS结构来完成同服务控制管理器SCM的通信;  
  2.   在列表中注册前面所说的服务控制处理函数;  
  3.   调用实际的处理函数。  
   
          为了完成上述功能,需要使用两个全局变量:  
  SERVICE_STATUS                   m_ServiceStatus;  
  SERVICE_STATUS_HANDLE           m_ServiceStatusHandle;  
   
          服务主程序gkeyServiceMain()能够象通常的c/c++里的main()函数一样接受命令行参数,并且接受参数的方式也完全一样。第一个参数argc包含了传递给服务的参数个数,同c/c++的main()一样至少有一个参数就是服务应用本身。第二个参数是一个字符指针数组的指针。同main()函数一样,数组的第一个值总是指向服务的名字。  
          使用SERVICE_STATUS数据结构记录服务的当前状态,并将状态及时通告给服务控制管理器SCM,使用一个API函数SetServiceStatus()来实现这一目标。SERVICE_STATUS的数据成结构员如下:  
   
  dwServiceType                 =   SERVICE_WIN32;                    
  dwCurrentState               =   SERVICE_START_PENDING;   //   试图启动(初始状态)  
  dwControlsAccepted       =   SERVICE_ACCEPT_STOP;       //   仅接收服务控制程序的启动/停止,服务控制程序通常在  
   
  Windows   NT下的控制面板或者Windows   2K下的管理工具,我们也可以设置服务接受暂停/继续功能。  
   
          在服务主程序gkeyServiceMain()的开始应该设置SERVICE_STATUS的状态字段dwCurrentState为SERVICE_START_PENDING,通知SCM服务处于运行状态。如果发生错误,应该发送SERVICE_STOPPED通知服务控制管理器SCM。缺省状态下,服务控制管理器SCM将监视服务的活动,如果2分钟之类没有发现进程活动就杀死这个服务。  
          使用API函数RegisterServiceCtrlHandler()设置服务控制管理器SCM的服务控制处理函数,这个函数需要两个参数,一个是服务名称字符串,一个是服务控制处理函数句柄。  
          现在要设置dwCurrentState为SERVICE_RUNNING用以通知服务已经启动。  
   
  服务控制处理函数  
   
          服务控制管理器SCM使用服务控制处理函数和服务程序进行通信来了解服务的诸如启动、停止、暂停或继续等用户指令,它主要包含一个switch语句来处理每种情况,调用相应的步骤来启动、急需、清除和中断进程。函数收到一个象SERVICE_CONTROL_PAUSE,   SERVICE_CONTROL_CONTINUE,   SERVICE_CONTROL_STOP,   SERVICE_CONTROL_INTERROGATE等操作码,就需要为每种指令提供相应的处理步骤。  
   
  安装/反安装  
   
          要安装一个服务,在系统注册时需要生成一些入口,通常使用Windows有现成的API而不是注册函数来完成这些步骤,这些函数有CreateService()和DeleteService()。为了安装服务,首先使用OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS)打开服务控制管理器SCM。然后调用CreateService()来建立服务,给出服务的名字,如果要删除指定的服务,也将需要使用这个名字删除。  
   
  例子代码如下:  
  //   创建服务  
  String   strSrvName   =   Application->ExeName;  
  SC_HANDLE   schService   =   CreateService(  
                  scm,  
                  "ccrunSrv",                       //   服务名称  
                  "ccrun's   Service",         //   服务详细说明  
                  SERVICE_ALL_ACCESS,  
                  SERVICE_WIN32_OWN_PROCESS   |   SERVICE_INTERACTIVE_PROCESS,  
                  SERVICE_AUTO_START,       //   以自动方式开始  
                  SERVICE_ERROR_NORMAL,  
                  strSrvName.c_str(),       //   Service本体程序路径,必须与具体位置相符  
                  NULL,  
                  NULL,  
                  NULL,  
                  NULL,  
                  NULL);  
  if(schService   !=   NULL)  
  {  
          CloseServiceHandle(schService);  
  }  
  //---------------------------------------------------------------------------  
  //   开始Service  
  sHandle   =   OpenService(scm,   "ccrunSrv",   SERVICE_START);  
  if(sHandle!=NULL)  
  {  
          StartService(sHandle,   0,   NULL);  
          CloseServiceHandle(sHandle);  
  }  
  //---------------------------------------------------------------------------  
  //   关闭服务管理器  
  CloseServiceHandle(scm);  
 

常用函数:
  ----在WindowsNT下   各种Service都存在service   control   manager   database中   因此我们可以通过对service   control   manager   database进行操作来实现对Service的编程。下面介绍常用的函数:  
   
  1:SC_HANDLE   OpenSCManager(LPCTSTR   lpszMachineName,   LPCTSTR   lpszDatabaseName,   DWORD   fdwDesiredAccess)    
  ----Open   SCManager   函数打开指定计算机上的service   control   manager   database。其中参数lpszMachineName指定计算机名   若为空则指定为本机。参数lpszDatabaseName指定要打开的service   control   manager   database,默认为空。  
   
  ----参数fdwDesiredAccess指定操作的权限,可以为下面取值之一  
  SC_MANAGER_ALL_ACCESS                 //   所有权限  
  SC_MANAGER_CONNECT                       //   允许连接service   control   manager  
  SC_MANAGER_CREATE_SERVICE         //   允许创建服务对象并把它加入service   control   manager   database  
  SC_MANAGER_ENUMERATE_SERVICE   //   允许枚举service   control   manager   database中的服务  
  SC_MANAGER_LOCK                             //   允许锁住service   control   manager   database  
  SC_MANAGER_QUERY_LOCK_STATUS   //   允许获取servicecontrolmanagerdatabase的封锁信息  
   
  ----函数返回值:函数执行成功则返回一个指向service   control   manager   database的句柄   失败则返回NULL。  
   
  2:SC_HANDLE   OpenService(SC_HANDLE   schSCManager,   LPCTSTR   lpszServiceName,   DWORD   fdwDesiredAccess)    
  ----OpenService函数打开指定的Service。  
  ----其中参数schSCManager是指向service   control   manager   database的句柄   由OpenSCManager函数返回。  
  ----参数lpszServiceName要打开的服务的名字   注意大小写。  
  ----参数fdwDesiredAccess指定操作的权限,可以为下面取值之一  
  SERVICE_ALL_ACCESS                         //   所有权限  
  SERVICE_CHANGE_CONFIG                   //   允许更改服务的配置  
  SERVICE_ENUMERATE_DEPENDENTS     //   允许获取依赖于该服务的其他服务  
  SERVICE_INTERROGATE                       //   允许立即获取服务状态  
  SERVICE_PAUSE_CONTINUE                 //   允许暂停和唤醒服务  
  SERVICE_QUERY_CONFIG                     //   允许获取服务配置  
  SERVICE_QUERY_STATU                       //   允许通过访问service   control   manager获取服务状态  
  SERVICE_START                                   //   允许启动服务  
  SERVICE_STOP                                     //   允许停止服务  
  SERVICE_USER_DEFINE_CONTROL       //   允许用户指定特殊的服务控制码  
  ----函数返回值:函数执行成功则返回指向某项服务的句柄   失败则返回NULL。  
   
  3:BOOL   QueryServiceStatus(SC_HANDLE   schService,LPSERVICE_STATUS   lpssServiceStatus)    
  ----QueryServiceStatus函数返回指定服务的当前状态。  
  ----其中参数schService是指向某项服务的句柄   由OpenService函数返回   且必须SERVICE_QUERY_STATUS的权限。  
  ----参数lpssServiceStatus中存放返回的服务状态信息   结构如下  
  typedef   struct   _SERVICE_STATUS  
  {  
          DWORD   dwServiceType                           //   服务类型  
          DWORD   dwCurrentState                         //   当前状态  
          DWORD   dwControlsAccepted                 //   服务可接受的控制码  
          DWORD   dwWin32ExitCode                       //   Win32出错代码  
          DWORD   dwServiceSpecificExitCode   //   服务出错代码  
          DWORD   dwCheckPoint                             //   用于跟踪服务长时间操作  
          DWORD   dwWaitHint                                 //   服务某一操作的最大允许时间,以毫秒为单位  
  }SERVICE_STATUS,   *LPSERVICE_STATUS;  
  ----函数返回值:函数执行成功则返回True,失败则返回False。  
   
  4:BOOLStartService(SC_HANDLE   schService,   DWORD   dwNumServiceArgs,   LPCTSTR   *   lpszServiceArgs)    
  ----StartService函数启动指定的服务。  
  ----其中参数schService是指向某项服务的句柄   由OpenService函数返回   且必须有SERVICE_START的权限。  
  ----dwNumServiceArgs为启动服务所需的参数的个数。  
  ----lpszServiceArgs为启动服务所需的参数。函数返回值:函数执行成功则返回True,失败则返回False。  
   
  5:BOOL   ControlService(SC_HANDLE   hService,   DWORD   dwControl,   LPSERVICE_STATUS   lpServiceStatus)    
  ----ControlService函数向Win32service发送控制码。  
  ----其中参数hService是指向某项服务的句柄   由OpenService函数返回。  
  ----参数dwControl为控制码   常用的有  
          SERVICE_CONTROL_STOP                 //   停止服务  
          SERVICE_CONTROL_PAUSE               //   暂停服务  
          SERVICE_CONTROL_CONTINUE         //   唤醒暂停的服务  
          SERVICE_CONTROL_INTERROGATE   //   刷新某服务的状态  
  ----参数lpServiceStatus指向SERVICE_STATUS结构   用于存放该服务最新的状态信息。  
  ----函数返回值:函数执行成功则返回True,失败则返回False。  
   
  6:BOOL   EnumServicesStatus(SC_HANDLE   hSCManager,   DWORD   dwServiceType,   DWORD   dwServiceState,  
                                                        LPENUM_SERVICE_STATUS   lpServices,   DWORD   cbBufSize,   LPDWORD   pcbBytesNeeded,  
                                                        LPDWORD   lpServicesReturned,   LPDWORD   lpResumeHandle)    
  ----EnumServicesStatus函数用于枚举NT下存在的Service。  
  ----其中参数hSCManager是指向service   control   manager   database的句柄   由OpenSCManager函数返回   且必须有SC_MANAGER_ENUMERATE_SERVICE的权限。  
  ----参数dwServiceType指定按服务的类型枚举。  
  ----参数dwServiceState指定按服务的状态枚举。  
  ----参数lpServices指向ENUM_SERVICE_STATUS结构   用于存放返回的服务的名字和状态信息。  
  ----参数cbBufSize返回参数lpServices的长度   以字节为单位。  
  ----参数pcbBytesNeeded返回获取剩余的Service所需字节的个数。  
  ----参数lpServicesReturned返回服务的个数。  
  ----参数lpResumeHandle   当第一次调用时该参数为0   当该函数再次被调用以获取另外的信息时   该参数表示下一个被读的Service。  
  ----函数返回值:函数执行成功则返回True,失败则返回False。  
  ----值得注意的是通常情况下该函数返回的结果为FALSE   我们可以调用GetLastError()来获取进一步信息。因为一台机器上有多种服务存在   所以GetLastError()应为ERROR_MORE_DATA   此时应再次调用EnumServicesStatus函数以获取正确的Service列表。  
posted @ 2009-01-09 18:19  pjh123  阅读(4202)  评论(0编辑  收藏  举报