本文內容包括如何通過C#代碼安裝Windows Service(exe文件,並非打包後的安裝文件)、判斷Service是否存在、獲得Service狀態及啓動停止Service。
創建Windows Service項目並Build得到exe文件,如何創建 Service 可參考 創建windows service 並打包成安裝文件。
一、 Windows服務的安裝和卸載
安裝和卸載服務可以使用 .NET 工具installutil.exe (eg:安裝-> installutil xxx.exe 卸載-> installutil /u xxx.exe),使用ManagedInstallerClass可以實現安裝卸載Windows 服務,ManagedInstallerClass 在System.Configuration.Install命名空間下。
實現如下:
1 /// <summary> 2 /// 使用Windows Service對應的exe文件 安裝Service 3 /// 和 installutil xxx.exe 效果相同 4 /// </summary> 5 /// <param name="installFile">exe文件(包含路徑)</param> 6 /// <returns>是否安裝成功</returns> 7 public static bool InstallServie(string installFile) 8 { 9 string[] args = { installFile }; 10 try 11 { 12 ManagedInstallerClass.InstallHelper(args); 13 return true; 14 } 15 catch 16 { 17 return false; 18 } 19 } 20 21 /// <summary> 22 /// 使用Windows Service對應的exe文件 卸載Service 23 /// 和 installutil /u xxx.exe 效果相同 24 /// </summary> 25 /// <param name="installFile">exe文件(包含路徑)</param> 26 /// <returns>是否卸載成功</returns> 27 public static bool UninstallService(string installFile) 28 { 29 string[] args = { "/u", installFile }; 30 try 31 { 32 // 根據文件獲得服務名,假設exe文件名和服務名相同 33 string tmp = installFile; 34 if (tmp.IndexOf('\\') != -1) 35 { 36 tmp = tmp.Substring(tmp.LastIndexOf('\\') + 1); 37 } 38 string svcName = tmp.Substring(0, tmp.LastIndexOf('.')); 39 // 在卸載服務之前 要先停止windows服務 40 StopService(svcName); 41 42 ManagedInstallerClass.InstallHelper(args); 43 return true; 44 } 45 catch 46 { 47 return false; 48 } 49 }
注意: 服務刪除前需要先停止服務,否則服務被標記爲禁用,並無法刪除。原因:系統刪除服務時,首先是標記服務爲“刪除”,等待服務的所有引用完全斷開後,服務才被完全刪除。當服務引用未完全斷開時,就刪除服務,系統將服務鎖死爲“禁用”狀態,並禁止其他操作,注意此時服務尚未完全刪除。所以對已刪除後立即重裝的服務,需要完全釋放與服務相關的所有引用,此時系統才真正完全刪除服務,之後才能再次安裝服務。如果出現服務禁用問題,檢查代碼,哪些地方有服務引用,同時檢查是否其他程序還在引用,也要終止這些引用程序。
二、Windows服務的狀態獲取和控制
使用ServiceController來獲取服務狀態或對服務進行控制。
ServiceController 表示 Windows 服務並允許連接到正在運行或者已停止的服務、對其進行操作或獲取有關它的信息。使用 ServiceController 類連接到現有服務並控制其行爲。當創建 ServiceController 類的實例時,設置其屬性,以便它與特定的 Windows 服務交互作用。然後可以使用此類來啓動、停止和以其他方式操作該服務。創建實例後,必須爲其設置兩個屬性來標識與其交互的服務:計算機名稱和要控制的服務的名稱。默認情況下,MachineName 設置爲本地計算機,因此不需要更改它,除非想將該實例設置爲指向另一臺計算機。
服務可以處理的命令集取決於該服務的屬性;例如,可以將服務的 CanStop 屬性設置爲 false。該設置使 Stop 命令在那個特定的服務上不可用;它禁用了必要的按鈕,使您無法從 SCM 中停止服務。如果試圖通過代碼停止服務,系統將引發錯誤,並顯示錯誤信息“未能停止 servicename”。
常用操作如下:
1. 獲得Service對應的ServiceController實例
1 /// <summary> 2 /// 獲得service對應的ServiceController對象 3 /// </summary> 4 /// <param name="serviceName">服務名</param> 5 /// <returns>ServiceController對象,若沒有該服務,則返回null</returns> 6 public static ServiceController GetService(string serviceName) 7 { 8 ServiceController[] services = ServiceController.GetServices(); 9 foreach (ServiceController s in services) 10 { 11 if (s.ServiceName == serviceName) 12 { 13 return s; 14 } 15 } 16 return null; 17 }
2. 檢查指定的服務是否存在
1 /// <summary> 2 /// 檢查指定的服務是否存在。 3 /// </summary> 4 /// <param name="serviceName">要查找的服務名字</param> 5 /// <returns>是否存在</returns> 6 public static bool ServiceExisted(string serviceName) 7 { 8 if (GetService(serviceName) == null) 9 { 10 return false; 11 } 12 else 13 { 14 return true; 15 } 16 }
3. 獲得服務詳細信息
1 /// <summary> 2 /// 獲得Service的詳細信息 3 /// </summary> 4 /// <param name="serviceName">服務名</param> 5 /// <returns>Service信息,保存在string中</returns> 6 public static string GetServiceInfo(string serviceName) 7 { 8 StringBuilder details = new StringBuilder(); 9 10 ServiceController sc = GetService(serviceName); 11 12 if (sc == null) 13 { 14 return string.Format("{0} 不存在!", serviceName); 15 } 16 17 details.AppendLine(string.Format("服務標識的名稱: {0}", sc.ServiceName)); 18 details.AppendLine(string.Format("服務友好名稱:{0}", sc.DisplayName)); 19 details.AppendLine(string.Format("服務在啓動後是否可以停止: {0}", sc.CanStop)); 20 details.AppendLine(string.Format("服務所駐留的計算機的名稱: {0}", sc.MachineName)); // "." 表示本地計算機 21 details.AppendLine(string.Format("服務類型: {0}", sc.ServiceType.ToString())); 22 details.AppendLine(string.Format("服務狀態: {0}", sc.Status.ToString())); 23 24 // DependentServices 獲取依賴於與此 ServiceController 實例關聯的服務的服務集。 25 StringBuilder dependentServices = new StringBuilder(); 26 foreach (ServiceController s in sc.DependentServices) 27 { 28 dependentServices.Append(s.ServiceName + ", "); 29 } 30 details.AppendLine(string.Format("依賴於與此 ServiceController 實例關聯的服務的服務: {0}", dependentServices.ToString())); 31 32 // ServicesDependedOn 此服務所依賴的服務集。 33 StringBuilder serviceDependedOn = new StringBuilder(); 34 foreach (ServiceController s in sc.ServicesDependedOn) 35 { 36 serviceDependedOn.Append(s.ServiceName + ", "); 37 } 38 details.AppendLine(string.Format("此服務所依賴的服務: {0}", serviceDependedOn.ToString())); 39 40 return details.ToString(); 41 }
4. 啓動服務
1 /// <summary> 2 /// 啓動服務 3 /// </summary> 4 /// <param name="serviceName">服務名</param> 5 /// <returns>是否啓動成功</returns> 6 public static bool StartService(string serviceName) 7 { 8 ServiceController sc = GetService(serviceName); 9 10 if (sc.Status != ServiceControllerStatus.Running) 11 { 12 try 13 { 14 sc.Start(); 15 sc.WaitForStatus(ServiceControllerStatus.Running); // 等待服務達到指定狀態 16 } 17 catch 18 { 19 return false; 20 } 21 } 22 23 return true; 24 }
5. 停止服務
1 /// <summary> 2 /// 停止服務 3 /// </summary> 4 /// <param name="serviceName">服務名</param> 5 /// <returns>是否停止服務成功,如果服務啓動後不可以停止,則拋異常</returns> 6 public static bool StopService(string serviceName) 7 { 8 ServiceController sc = GetService(serviceName); 9 10 if (!sc.CanStop) 11 { 12 throw new Exception(string.Format("服務{0}啓動後不可以停止.", serviceName)); 13 } 14 15 if (sc.Status != ServiceControllerStatus.Stopped) 16 { 17 try 18 { 19 sc.Stop(); 20 sc.WaitForStatus(ServiceControllerStatus.Stopped); // 等待服務達到指定狀態 21 } 22 catch 23 { 24 return false; 25 } 26 } 27 28 return true; 29 }