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

原创 2004年01月04日 10:13:00
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;

Android 服务类Service 的详细学习

上一篇说到了通知栏Notification,提起通知栏,不得让人想到Service以及BroadcastReceive,作为android的4大组建的2个重要成员,我们没少和它们打交道。它们可以在无形...
  • vipzjyno1
  • vipzjyno1
  • 2014年05月19日 08:07
  • 46086

【Android服务】制作无法销毁的Service

今天面试没表现好啊,现在来好好研究下这个问题:如何制作Android无法销毁的Service?(虽然在用户的角度上看,这样的开发显得很无赖) 事实上,解决这个还需要一个帮助,运用广播。 在启动Se...
  • u011669081
  • u011669081
  • 2016年01月24日 20:06
  • 1803

thrift的用法入门,实现C#和java通信哦

thrift的简介,就是实现J
  • michaelhit
  • michaelhit
  • 2014年10月25日 21:48
  • 1128

java Service Wrapper 将jar注册为windows系统服务

java Service Wrapper项目用于将jar包注册为系统服务,支持windows及linux等系统。 项目下载地址:http://wrapper.tanukisoftware.com/do...
  • wangchao729251283
  • wangchao729251283
  • 2016年11月15日 22:40
  • 1146

windows安装Apache,注册服务出现“(OS 5)拒绝访问。 : AH00369: Failed to open the WinNT service manager..."

我下的是压缩版的apache,而不是msi的,所以需要自己注册服务,结果在cmd命令行输入服务命令的时候出现了如下的错误: 百思不得其解,最后看错误提示:你可能没有用管理员身份运行。 所以我选择用...
  • qq_36730963
  • qq_36730963
  • 2017年05月08日 23:13
  • 212

项目笔记---Windows Service调用Windows API问题

转于:http://www.cnblogs.com/cuiyansong/p/4318551.html 概要   此文来自于最近一个“诡异”的Windows API调用发现Wind...
  • wk89665944
  • wk89665944
  • 2016年12月29日 13:42
  • 182

http://www.divakk.co.jp/aoyagi/csharp_tips_wbzone.html

http://www.divakk.co.jp/aoyagi/csharp_tips_wbzone.html   C# Tips -AxWebBrowserのIInternetSecurityM...
  • siumennel
  • siumennel
  • 2011年09月20日 01:03
  • 694

CSharp Tips:怎样创建COM的实例

CSharp Tips:怎样创建COM的实例 标签: interfacec#null工具windowsobject 2006-05-19 16:43 5247人阅读 评论(1) ...
  • autumn20080101
  • autumn20080101
  • 2016年09月12日 17:47
  • 138

使用Windows API操作注册表

来自:http://blog.csdn.net/yunboy4/article/details/4595230 使用Windows API操作注册表 分类: Win322009...
  • angelxf
  • angelxf
  • 2012年05月02日 17:30
  • 3841

Windows API:注册表

1·RegClose()    原形:LONG RegCloseKey(    HKEY hKey  // 释放已经打开的注册表句柄       );    返回值:不成功返回非0,成功返回ERROR...
  • zhaohaiyang93
  • zhaohaiyang93
  • 2015年05月22日 18:50
  • 40
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:CSharp Tips:调用API注册和注销Windows Service
举报原因:
原因补充:

(最多只允许输入30个字)