最近在爲一款C/S架構的科研軟件開發雲計算版,須要用到WCF,考慮到不須要什麼界面以及穩定性,無人值守性,準備用Windows Service做爲宿主,無奈Windows Service的安裝太爲繁複,就想如何經過C#代碼完成Windows服務的安裝及配置,在網上找了些資料,大多都是很是簡單的代碼,並無一個完整的示例,可能一些初學者看起來不是很清晰,特別作了這個Demo!html
首先創建項目,結構很是簡單,一個控制檯應用程序,添加一個類庫(ServiceHelper.cs 安裝、卸載服務輔助類),一個Window服務(WindowsService.cs)數據庫
項目的思路就是,啓動控制檯應用程序,自動識別是應該以服務身份啓動服務仍是以控制檯應用身份啓動服務配置架構
首先來看Windows服務定義ide
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace ConsoleWithWindowsService { partial class WindowsService : ServiceBase { public WindowsService() { InitializeComponent(); } protected override void OnStart(string[] args) { System.IO.File.AppendAllText(@"D:\Log.txt", " Service Start :" + DateTime.Now.ToString());
new Task(() =>
{函數
// TODO: 在此處添加代碼 這裏你本身的代碼 使用Task 就能夠解決原來做者那個啓動服務後須要循環60秒判斷任務是否開始等一系列問題
}).Start();雲計算
} protected override void OnStop() { // TODO: 在此處添加代碼以執行中止服務所需的關閉操做。 System.IO.File.AppendAllText(@"D:\Log.txt", " Service Stop :" + DateTime.Now.ToString()); } } }
爲了簡單演示,這裏我沒有作什麼工做,只是簡單的往D盤寫入了一個日誌,證實服務正常工做就好了spa
而後來看Windows服務的輔助類命令行
using System; using System.Collections; using System.Collections.Generic; using System.Configuration.Install; using System.Linq; using System.Reflection; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace ConsoleWithWindowsService { public class ServiceHelper { /// <summary> /// 服務是否存在 /// </summary> /// <param name="serviceName"></param> /// <returns></returns> public static bool IsServiceExisted(string serviceName) { ServiceController[] services = ServiceController.GetServices(); foreach (ServiceController s in services) { if (s.ServiceName == serviceName) { return true; } } return false; } /// <summary> /// 啓動服務 /// </summary> /// <param name="serviceName"></param> public static void StartService(string serviceName) { if (IsServiceExisted(serviceName)) { System.ServiceProcess.ServiceController service = new System.ServiceProcess.ServiceController(serviceName); if (service.Status != System.ServiceProcess.ServiceControllerStatus.Running && service.Status != System.ServiceProcess.ServiceControllerStatus.StartPending) { service.Start(); for (int i = 0; i < 60; i++) { service.Refresh(); System.Threading.Thread.Sleep(1000); if (service.Status == System.ServiceProcess.ServiceControllerStatus.Running) { break; } if (i == 59) { throw new Exception("Start Service Error:" + serviceName); } } } } } /// <summary> /// 獲取服務狀態 /// </summary> /// <param name="serviceName"></param> /// <returns></returns> public static ServiceControllerStatus GetServiceStatus(string serviceName) { System.ServiceProcess.ServiceController service = new System.ServiceProcess.ServiceController(serviceName); return service.Status; } /// <summary> /// 配置服務 /// </summary> /// <param name="serviceName"></param> /// <param name="install"></param> public static void ConfigService(string serviceName, bool install) { TransactedInstaller ti = new TransactedInstaller(); ti.Installers.Add(new ServiceProcessInstaller { Account = ServiceAccount.LocalSystem }); ti.Installers.Add(new ServiceInstaller { DisplayName = serviceName, ServiceName = serviceName, Description = "MicroID微檢系統數據後臺服務", ServicesDependedOn = new string[] { "MSSQLSERVER" },//前置服務 StartType = ServiceStartMode.Automatic//運行方式 }); ti.Context = new InstallContext(); ti.Context.Parameters["assemblypath"] = "\"" + Assembly.GetEntryAssembly().Location + "\" /service"; if (install) { ti.Install(new Hashtable()); } else { ti.Uninstall(null); } } } }
這裏有四個函數,分別是用於驗證指定服務是否存在,啓動服務,獲取指定服務狀態和最關鍵的服務安裝及卸載(服務配置)日誌
驗證服務存在性及獲取服務狀態沒什麼好說的,啓動服務由於會有一個延時,經過一個循環模擬等待服務啓動的這段時間,超時即爲啓動失敗,重點是第四個服務配置函數code
public static void ConfigService(string serviceName, bool install)
這裏咱們須要傳入你但願命名的服務名稱,經過一個bool值判斷是安裝仍是卸載服務,項目中我統一都命名爲MyService,接下來看下面這段代碼
ti.Installers.Add(new ServiceInstaller { DisplayName = serviceName, ServiceName = serviceName, Description = "MicroID微檢系統數據後臺服務", ServicesDependedOn = new string[] { "MSSQLSERVER" },//前置服務 StartType = ServiceStartMode.Automatic//運行方式 });
在這段代碼中,爲服務設置了友好名(DisplayName即爲任務管理器、服務管理器中看到的服務名),服務名,說明,前置服務,以及運行方式
這裏的前置服務是告訴Windows,啓動個人時候,記得要先等待SQLServer啓動,我須要用到它,若是你的服務須要訪問SQLServer數據庫,那可千萬不要忘了這裏
最後是控制檯應用程序的入口
using System; using System.Collections.Generic; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace ConsoleWithWindowsService { class Program { static void Main(string[] args) { //帶參啓動運行服務 if (args.Length > 0) { try { ServiceBase[] serviceToRun = new ServiceBase[] { new WindowsService() }; ServiceBase.Run(serviceToRun); } catch (Exception ex) { System.IO.File.AppendAllText(@"D:\Log.txt", "\nService Start Error:" + DateTime.Now.ToString()+"\n"+ex.Message); } } //不帶參啓動配置程序 else { StartLable: Console.WriteLine("請選擇你要執行的操做——1:自動部署服務,2:安裝服務,3:卸載服務,4:驗證服務狀態,5:退出"); Console.WriteLine("————————————————————"); ConsoleKey key = Console.ReadKey().Key; if (key == ConsoleKey.NumPad1 || key == ConsoleKey.D1) { if (ServiceHelper.IsServiceExisted("MyService")) { ServiceHelper.ConfigService("MyService", false); } if (!ServiceHelper.IsServiceExisted("MyService")) { ServiceHelper.ConfigService("MyService", true); } ServiceHelper.StartService("MyService"); goto StartLable; } else if (key == ConsoleKey.NumPad2 || key == ConsoleKey.D2) { if (!ServiceHelper.IsServiceExisted("MyService")) { ServiceHelper.ConfigService("MyService", true); } else { Console.WriteLine("\n服務已存在......"); } goto StartLable; } else if (key == ConsoleKey.NumPad3 || key == ConsoleKey.D3) { if (ServiceHelper.IsServiceExisted("MyService")) { ServiceHelper.ConfigService("MyService", false); } else { Console.WriteLine("\n服務不存在......"); } goto StartLable; } else if (key == ConsoleKey.NumPad4 || key == ConsoleKey.D4) { if (!ServiceHelper.IsServiceExisted("MyService")) { Console.WriteLine("\n服務不存在......"); } else { Console.WriteLine("\n服務狀態:" + ServiceHelper.GetServiceStatus("MyService").ToString()); } goto StartLable; } else if (key == ConsoleKey.NumPad5 || key == ConsoleKey.D5) { } else { Console.WriteLine("\n請輸入一個有效鍵!"); Console.WriteLine("————————————————————"); goto StartLable; } } } } }
有了這三部分,就算完成啦,當咱們生成項目後,啓動應用程序,這時會默認啓動控制檯應用程序,由於arg[] 參數爲空,有人可能有疑問,那服務又如何啓動呢,注意Windows服務輔助類中的這句代碼
ti.Context.Parameters["assemblypath"] = "\"" + Assembly.GetEntryAssembly().Location + "\" /service";
咱們在爲服務註冊的時候,在後面加了"/service"這個參數,也就是說,當咱們直接啓動可執行文件時,這個參數爲空,程序會啓動控制檯應用程序,而咱們註冊的服務會攜帶這個參數,你會在後面看到效果,每次服務啓動時由於都帶了這個參數,程序會自動執行下面的代碼來直接啓動服務,由此作到了兩種程序的動態選擇
///帶參啓動運行服務 if (args.Length > 0) { try { ServiceBase[] serviceToRun = new ServiceBase[] { new WindowsService() }; ServiceBase.Run(serviceToRun); } catch (Exception ex) { System.IO.File.AppendAllText(@"D:\Log.txt", "Service Start Error:" + DateTime.Now.ToString()+"\n"+ex.Message); } }
還有最重要的一點:Win7+系統,因爲權限問題,須要爲執行程序設置以管理員方式運行,經過VS直接Debug是沒法執行成功的,這也是不少網上的老舊資料忽略的一點
如下是運行效果演示
這裏我首先獲取了一下服務狀態,顯示服務並不存在,而後經過自動部署(其實就是判斷服務狀態,不存在則安裝,存在則先卸載再安裝,最後啓動服務),完成後,再次獲取服務狀態,顯示Running!!!
而後咱們進入服務管理器看下
沒錯,就是它了,跟咱們上面設置的友好名,以及說明一致,而後咱們點開這個服務的屬性
你是否是已經看到玄機了,可執行文件路徑後面帶上了參數,這就是動態選擇啓動控制檯程序仍是啓動服務的關鍵,同時依存關係裏面有了SQLServer,接下來咱們作最後一步驗證,打開D盤日誌文件
至此,證實整套的服務安裝,啓動均已完成,是否是很方便,之後不再用經過命令行去安裝,卸載了,每次均可以直接啓動這個控制檯應用程序,就能夠完成對服務的配置!!!
原創文章,轉載請註明出處
最後附上源碼
開發環境:Visual studio 2015 / .Net Framework 4.5.2