Windows Service:SC 和 InstallUtil 區別

weixin_34279184發表於2011-07-18

就是在部署和解除安裝 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的時候做了以下幾件事情:

  1. LogMessage: 記錄Install 日誌
  2. CheckEnvironment : 檢查安裝環境,主要是系統的OS版本和Service環境的OS版本是否一致
  3. 查詢父級Installer(Parent屬性)以及父級Installer的子Installer中有沒有ServiceProcessInstaller,並將找到的ServiceProcessInstaller賦值給該ServiceInstaller的Parent屬性,同時 將ServiceProcessInstaller中有關Account的資訊給拷貝過來
  4. 準備相應的引數,以備呼叫Windows32 API 來建立服務,呼叫CreateService API建立服務
  5. 設定服務的相關配置,做好 Log
  6. 執行父類的Install()

    serviceInstaller1在UnInstall的時候(以下省略了日誌等不重要的步驟):

  1. 先執行父類的UnInstall()
  2. 利用OpenSCManager API 開啟服務管理IntPtr
  3. 利用OpenService API 找到相應的服務
  4. 利用DeleteService API 刪除找到的服務
  5. 關閉 服務管理IntPtr
  6. 利用 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:

ExpandedBlockStart.gif程式碼

[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行為的類,明天我把程式碼給貼出來!

相關文章