本來打算這篇寫具體的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 (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:
代碼
[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
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行為的類,明天我把代碼給貼出來!