CSharp Tips:调用API注册和注销Windows Service

类别:.NET开发 点击:0 评论:0 推荐:
0、写在前面
    DotNET平台下的类库封装的相当完善,普通的应用完全可以利用类库完成所有的工作。对于Windows Service的支持也是一样,只需要继承DotNET下提供的ServiceBase就可以创建Windows的Service,调用ServiceControl类的方法就可以控制Service的启动和关闭,非常容易。
    然而生成了一个Service类型的应用程序之后,必须在SCM(Service Control Manager)中注册,才能够被当作一个Service被系统调用。但是对于Service的注册和注销却比较麻烦,DotNET的确也提供了对应的类ServiceInstaller和ServiceProcessInstaller,但是只能够和Installer的安装过程集成使用,不能够单独在普通应用程序中调用,也就是说为了安装一个Service用作测试,我不得不创建一个安装包。
    怎样利用API向SCM注册以及注销一个Service呢,下面就简单的介绍一下。
 
1、实现概述
    打开Control Panel中Service的管理工具,可以看到本机所有已注册的Service,选择一个Service的属性会发现一个Service有很多设置项:名称、描述、类型、运行帐号等。所有这些都可以通过调用API函数来设定。
    在Service的API中,每一个服务的实例通过句柄来表示,而需要服务的句柄之前必须要先要的到SCM的句柄(Handle),所以所有的调用都是通过OpenSCManager开始,成功的调用OpenSCManager后将获得一个SCM的句柄。如果是注册Service,那么利用SCM句柄调用CreateService来创建一个新的服务;如果是注销Service,则调用OpenService函数,获得一个已经存在的Service的句柄,然后利用这个服务句柄调用DeleteService注销服务。最后通过CloseServiceHandle关闭Service的句柄和SCM的句柄。
    过程很简单,在CSharp下比较麻烦的地方就是API的声明,下面会一一提到。
 
2、注册Service
    前面已经提到注册Service一共用到了三个API,OpenSCManager、CreateService和CloseServiceHandle。使用之前先介绍一下这些API的声明。
  [DllImport("advapi32.dll")]
  public static extern System.IntPtr OpenSCManager(
    System.String lpMachineName,
    System.String lpDatabaseName,
    System.UInt32 dwDesiredAccess
   );
  [DllImport("advapi32.dll",EntryPoint = "CreateServiceA")]
  public static extern System.IntPtr CreateService(
    System.IntPtr hSCManager,
    System.String lpServiceName,
    System.String lpDisplayName,
    System.UInt32 dwDesiredAccess,
    System.UInt32 dwServiceType,
    System.UInt32 dwStartType,
    System.UInt32 dwErrorControl,
    System.String lpBinaryPathName,
    System.String lpLoadOrderGroup,
    System.IntPtr lpdwTagId,
    System.String lpDependencies,
    System.String lpServiceStartName,
    System.String lpPassword
   );
  [DllImport("advapi32.dll")]
  public static extern System.Boolean CloseServiceHandle(
    System.IntPtr hSCObject
   );
    这个了关于Service的API都是在Advapi32.dll中实现的,函数的原型可以自行查找头文件,在Winsvc和Winbase中。
    熟悉Windows编程的话一定会了解,句柄类型就是一个32位的整型,在DotNET下用IntPtr来声明,DWORD对应UINT32,LPCSTR对应String类型,唯一需要强调的是CreateService这个函数的lpdwTagId,是一个DWORD*,这里声明也IntPtr,因为在调用中绝大多数情况下传递NULL值,如果用out UINT32,无法传递NULL值。
    仔细看CreateService中声明可以发现,这个函数真的可以做很多事情,其中包括Service的名称(lpServiceName,服务的标识,调用OpenSerive等函数时用到)、显示名(lpDisplayName,就是我们在Service的管理工具中看到的名称)、服务类型(dwServiceType,指定服务的运行方式:独立进程、共享进程、驱动程序还是交互式登录模式等等)、启动类型(dwStartType,自动、手动还是禁止等等)、服务失败的严重性(dwErrorControl)、实现服务代码的二进制文件的路径(lpBinaryPathName)、加载顺序组的名称(lpLoadOrderGroup)、接受Tag标志码(lpdwTagId)、依赖服务的名称组(lpDependencies)、启动服务的帐号(lpServiceStartName,如果为NULL,表示使用LocalSystem)、启动服务帐号的口令(lpPassword)。如果调用成功,那么将返回一个非0的句柄,表示服务注册成功。
    看了上面的一系列属性说明,大家也许发现还少了一样,就是在Service的管理工具中最长最醒目的一栏Description,Description的设置需要调用另一个API,如下:
  public static extern System.Boolean ChangeServiceConfig2(
    System.IntPtr hService,
    System.UInt32 dwInfoLevel,
    ref System.String lpInfo
   );
    其中lpInfo的声明原型是一个LPVOID,如果设置Description属性的话指向的是一个结构:
 typedef struct _SERVICE_DESCRIPTION {
   LPTSTR
lpDescription;
 } SERVICE_DESCRIPTION, *LPSERVICE_DESCRIPTION;
    这个结构里面包含了一个字符指针,也就是说需要在函数调用时传递一个指向字符指针的指针,偷懒起见,ref System.String就足够了,无需再定义结构了,呵呵!
    完整的例子如下,常量定义见本文最后:
  private static System.Boolean RegistService()
  {
   System.Boolean  fRet = false;
   System.IntPtr  hServiceManager = IntPtr.Zero,hService = IntPtr.Zero;
   System.String  sServicePath = null,sDesc = null;
 
   sServicePath = Application.StartupPath + @"\sampleservice.exe";
   hServiceManager = OpenSCManager(Environment.MachineName,null,SC_MANAGER_ALL_ACCESS);
   if (hServiceManager != IntPtr.Zero)
   {
    hService = CreateService(hServiceManager,"sampleservice","Sample Service",SERVICE_ALL_ACCESS,
     SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,
     sServicePath,null,IntPtr.Zero,null,null,null);
    if (hService != IntPtr.Zero)
    {
     sDesc = "This is a sample service.";
     fRet = ChangeServiceConfig2(hService,SERVICE_CONFIG_DESCRIPTION,ref sDesc);
     CloseServiceHandle(hService);
     hService = IntPtr.Zero;
    }
    CloseServiceHandle(hServiceManager);
    hServiceManager = IntPtr.Zero;
   }
   return fRet;
  }
3、注销Service
    相对于注册,注销就简单很多了,用到了OpenSCManager、OpenService、DeleteService和CloseServiceHandle四个API,另两个API的声明如下:
  [DllImport("advapi32.dll")]
  public static extern System.IntPtr OpenService(
    System.IntPtr hSCManager,
    System.String lpServiceName,
    System.UInt32 dwDesiredAccess
   );
  [DllImport("advapi32.dll")]
  public static extern System.Boolean DeleteService(
    System.IntPtr hService
   );
    比较简单没什么可多说的看一个完整的例子:
  private static System.Boolean UnRegistService()
  {
   System.Boolean  fRet = false;
   System.IntPtr  hServiceManager = IntPtr.Zero,hService = IntPtr.Zero;
 
   hServiceManager = OpenSCManager(Environment.MachineName,null,SC_MANAGER_ALL_ACCESS);
   if (hServiceManager != IntPtr.Zero)
   {
    hService = OpenService(hServiceManager,"sampleservice",SERVICE_ALL_ACCESS);
    if (hService != IntPtr.Zero)
    {
     fRet = DeleteService(hService);
     CloseServiceHandle(hService);
     hService = IntPtr.Zero;
    }
    CloseServiceHandle(hServiceManager);
    hServiceManager = IntPtr.Zero;
   }
   return fRet;
  }
4、最后
    可以在实现Service的工程中增加一下额外的处理,Main函数中判断一下调用参数,如果是“-I”,则表示注册Service,如果是“-U”表示注销Service,反之则作为Service正常运行。
 
5、API和常量的声明
    本文所用到的所有API和常量如下:
  // declare APIs for service
  [DllImport("advapi32.dll")]
  public static extern System.IntPtr OpenSCManager(
    System.String lpMachineName,
    System.String lpDatabaseName,
    System.UInt32 dwDesiredAccess
   );
  [DllImport("advapi32.dll",EntryPoint = "CreateServiceA")]
  public static extern System.IntPtr CreateService(
    System.IntPtr hSCManager,
    System.String lpServiceName,
    System.String lpDisplayName,
    System.UInt32 dwDesiredAccess,
    System.UInt32 dwServiceType,
    System.UInt32 dwStartType,
    System.UInt32 dwErrorControl,
    System.String lpBinaryPathName,
    System.String lpLoadOrderGroup,
    System.IntPtr lpdwTagId,
    System.String lpDependencies,
    System.String lpServiceStartName,
    System.String lpPassword
   );
  [DllImport("advapi32.dll")]
  public static extern System.IntPtr OpenService(
    System.IntPtr hSCManager,
    System.String lpServiceName,
    System.UInt32 dwDesiredAccess
   );
  [DllImport("advapi32.dll")]
  public static extern System.Boolean DeleteService(
    System.IntPtr hService
   );
  [DllImport("advapi32.dll")]
  public static extern System.Boolean CloseServiceHandle(
    System.IntPtr hSCObject
   );
  [DllImport("advapi32.dll")]
  public static extern System.Boolean ChangeServiceConfig2(
    System.IntPtr hService,
    System.UInt32 dwInfoLevel,
    ref System.String lpInfo
   );
 
  public const System.UInt32 STANDARD_RIGHTS_REQUIRED = 0xF0000;
  // Service Control Manager object specific access types
  public const System.UInt32 SC_MANAGER_CONNECT = 0x0001;
  public const System.UInt32 SC_MANAGER_CREATE_SERVICE = 0x0002;
  public const System.UInt32 SC_MANAGER_ENUMERATE_SERVICE = 0x0004;
  public const System.UInt32 SC_MANAGER_LOCK = 0x0008;
  public const System.UInt32 SC_MANAGER_QUERY_LOCK_STATUS = 0x0010;
  public const System.UInt32 SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020;
  public const System.UInt32 SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
    SC_MANAGER_CONNECT            |
    SC_MANAGER_CREATE_SERVICE     |
    SC_MANAGER_ENUMERATE_SERVICE  |
    SC_MANAGER_LOCK               |
    SC_MANAGER_QUERY_LOCK_STATUS  |
    SC_MANAGER_MODIFY_BOOT_CONFIG;
  // Service object specific access type
  public const System.UInt32 SERVICE_QUERY_CONFIG = 0x0001;
  public const System.UInt32 SERVICE_CHANGE_CONFIG = 0x0002;
  public const System.UInt32 SERVICE_QUERY_STATUS = 0x0004;
  public const System.UInt32 SERVICE_ENUMERATE_DEPENDENTS = 0x0008;
  public const System.UInt32 SERVICE_START = 0x0010;
  public const System.UInt32 SERVICE_STOP = 0x0020;
  public const System.UInt32 SERVICE_PAUSE_CONTINUE = 0x0040;
  public const System.UInt32 SERVICE_INTERROGATE = 0x0080;
  public const System.UInt32 SERVICE_USER_DEFINED_CONTROL = 0x0100;
  public const System.UInt32 SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED     |
    SERVICE_QUERY_CONFIG         |
    SERVICE_CHANGE_CONFIG        |
    SERVICE_QUERY_STATUS         |
    SERVICE_ENUMERATE_DEPENDENTS |
    SERVICE_START                |
    SERVICE_STOP                 |
    SERVICE_PAUSE_CONTINUE       |
    SERVICE_INTERROGATE          |
    SERVICE_USER_DEFINED_CONTROL;
  // service type
  public const System.UInt32 SERVICE_KERNEL_DRIVER = 0x00000001;
  public const System.UInt32 SERVICE_FILE_SYSTEM_DRIVER = 0x00000002;
  public const System.UInt32 SERVICE_ADAPTER = 0x00000004;
  public const System.UInt32 SERVICE_RECOGNIZER_DRIVER = 0x00000008;
  public const System.UInt32 SERVICE_DRIVER = SERVICE_KERNEL_DRIVER |
    SERVICE_FILE_SYSTEM_DRIVER |
    SERVICE_RECOGNIZER_DRIVER;
  public const System.UInt32 SERVICE_WIN32_OWN_PROCESS = 0x00000010;
  public const System.UInt32 SERVICE_WIN32_SHARE_PROCESS = 0x00000020;
  public const System.UInt32 SERVICE_WIN32 = SERVICE_WIN32_OWN_PROCESS |
    SERVICE_WIN32_SHARE_PROCESS;
  public const System.UInt32 SERVICE_INTERACTIVE_PROCESS = 0x00000100;
  public const System.UInt32 SERVICE_TYPE_ALL = SERVICE_WIN32  |
    SERVICE_ADAPTER |
    SERVICE_DRIVER  |
    SERVICE_INTERACTIVE_PROCESS;
  // Start Type
  public const System.UInt32 SERVICE_BOOT_START = 0x00000000;
  public const System.UInt32 SERVICE_SYSTEM_START = 0x00000001;
  public const System.UInt32 SERVICE_AUTO_START = 0x00000002;
  public const System.UInt32 SERVICE_DEMAND_START = 0x00000003;
  public const System.UInt32 SERVICE_DISABLED = 0x00000004;
  // Error control type
  public const System.UInt32 SERVICE_ERROR_IGNORE = 0x00000000;
  public const System.UInt32 SERVICE_ERROR_NORMAL = 0x00000001;
  public const System.UInt32 SERVICE_ERROR_SEVERE = 0x00000002;
  public const System.UInt32 SERVICE_ERROR_CRITICAL = 0x00000003;
  // Info levels for ChangeServiceConfig2 and QueryServiceConfig2
  public const System.UInt32 SERVICE_CONFIG_DESCRIPTION = 1;
  public const System.UInt32 SERVICE_CONFIG_FAILURE_ACTIONS = 2;

本文地址:http://com.8s8s.com/it/it45163.htm