本来打算这篇写具体的Jobs模块的实现,但是回头一考虑,好像还忘记了一个重要的问题,就是在部署和卸载 Windows Service 的时候,我介绍了两种不同的命令:SC和InstallUtil,为什么在使用SC的时候,内部不需要创建ProjectInstaller,而使用InstallUtil的时候,却一定要创建ProjectInstaller,带着疑问,查阅了一些资料,终于找出了一些线索,不足的地方还请懂的指点一下。
原来以为SC是最底层的命令行,而InstallUtil是调用SC命令来创建的,可惜我犯了个比较低级的错误,所有的一切应该都来源于Windows API,不管是SC,还是InstallUtil。
我们先来看看InstallUtil,MSDN的解释是:
安装程序工具使您得以通过在执行指定程序集中的安装程序组件来安装和卸载服务器资源,。此工具与 System.Configuration.Install 命名空间中的类一起工作。
Installutil.exe 使用反射检查指定的程序集并查找将 RunInstallerAttribute 设置为 true 的所有 Installer 类型。然后此工具在 Installer 类型的每个实例上执行 Install 方法或 Uninstall 方法。Installutil.exe 以事务性方式执行安装;如果有一个程序集未能安装,则 Installutil.exe 回滚其他所有程序集的安装。卸载不是事务性的。
这样一看,InstallUtil应该是调用了我们在Windows Service中添加的ProjectInstaller中的serviceInstaller1和serviceProcessInstaller1,后两者是最终部署和卸载Windows Service的Installer,那么serviceInstaller1和serviceProcessInstaller1之间又是什么关系呢??
用Reflect 查看了下这两者的源码,我目前的结论大概是: serviceProcessInstaller1主要是存储了一些与Account相关的信息,并且在Install的时候检查Account的权限;但是serviceInstaller1则是用来存储与服务相关的信息,比如:服务名称,服务描述,启动模式,等等,并且在Install的时候创建服务;
serviceInstaller1在Install的时候做了以下几件事情:
- LogMessage: 记录Install 日志
- CheckEnvironment : 检查安装环境,主要是系统的OS版本和Service环境的OS版本是否一致
- 查找父级Installer(Parent属性)以及父级Installer的子Installer中有没有ServiceProcessInstaller,并将找到的ServiceProcessInstaller赋值给该ServiceInstaller的Parent属性,同时 将ServiceProcessInstaller中有关Account的信息给拷贝过来
- 准备相应的参数,以备调用Windows32 API 来创建服务,调用CreateService API创建服务
- 设置服务的相关配置,做好 Log
- 执行父类的Install()
serviceInstaller1在UnInstall的时候(以下省略了日志等不重要的步骤):
- 先执行父类的UnInstall()
- 利用OpenSCManager API 打开服务管理IntPtr
- 利用OpenService API 找到相应的服务
- 利用DeleteService API 删除找到的服务
- 关闭 服务管理IntPtr
- 利用 ServiceController 类查看该服务是否还存在,如果还存在的话,设法Stop该服务。(这个地方有些不明白,为啥调用DeleteService API 后还需要做这进一步的检查并且让其Stop呢??而不是Throw Exception)
至此,serviceInstaller1的Install和UnInstall过程都已完成,InstallUtil部署服务的核心基本已经清楚。 这里在UnInstall的时候,有的时候用InstallUtil.exe 来对服务进行UnInstall的时候,可能会出现卸载不掉,估计可能与两个地方有原因:
1:InstallUtil.exe在UnInstall的时候是非事务执行的
2:就是在上面UnInstall的第6步;
但是到底是什么原因,我还是停留在上面两点的猜测上,不知哪位可以解释一下!??
而对于SC,则比较简单了,以下解释来自 MSDN :
远程创建,并从命令行启动服务,您可以使用资源工具包中包含 SC 工具 (Sc.exe)。
使用 Sc.exe 可以帮助开发的 Windows 服务。Sc.exe,资源工具包中提供实现对所有在 Windows 服务的控件应用程序编程接口 (API) 函数的调用。
这说明了SC是直接调用Windows API来实现Windows Service的安装,卸载,查询等等一系列操作和控制的。
对此,我们可以大概的了解了SC.exe和InstallUtil.exe对Windows Service操作的本质了。
接下来,我会对怎么样调用Windows API对服务进行操作进行一个大概的介绍,以及ServiceControl的介绍。
ServiceControl 类主要是用来对Windows Service进行控制的一个.NET类,比如:Stop(),Start(),Pause(),Refresh()等;
而调用Windows API可以对Windows Service的 名称,描述,状态,启动类型,运行帐号等等的控制,其中主要包括以下几个API:
public static extern IntPtr CreateService(IntPtr databaseHandle, string serviceName, string displayName, int access, int serviceType, int startType, int errorControl, string binaryPath, string loadOrderGroup, IntPtr pTagId, string dependencies, string servicesStartName, string password);
[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern IntPtr OpenSCManager(string machineName, string databaseName, int access);
[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern IntPtr OpenService(IntPtr databaseHandle, string serviceName, int access);
[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern bool DeleteService(IntPtr serviceHandle);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern bool CloseServiceHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern bool ChangeServiceConfig2(IntPtr serviceHandle, uint infoLevel, ref SERVICE_DESCRIPTION serviceDesc);
在Service 的API中,每一个服务的实例都通过句柄表示,而需要服务的句柄之前必须先要得到SCM的句柄,所以所有的调用都是通过OpenSCManager开始,成功的调用OpenSCManager后会获得一个SCM句柄;如果是注册Service,那么利用SCM句柄调用CreateService来创建一个新的服务;如果是删除Service,则调用OpenService方法,获得一个已经存在的Service句柄,然后利用这个句柄调用DeleteService来删除Service,最后还的通过调用CloseServiceHandle来关闭Service句柄和SCM的句柄。而ChangeServiceConfig2 主要用来设置服务的Description的。
这样,我们可以通过API来实现一个控制Windows Service行为的类(包括 创建,注销,停止,启动,等等)。
当然,如果我们用ServiceInstaller,ServiceProcessInstaller来创建和删除服务,用ServiceControl来管理服务。
呵呵,用API实现控制Windows Service行为的类,明天我把代码给贴出来!