.NET Framework中提供了現成的類庫可以很方便的實現對windows服務的安裝、卸載、啟動、停止、獲取運行狀態等功能。這些類都在System.ServiceProcess命名空間下。
安裝window服務
using (AssemblyInstaller installer = new AssemblyInstaller()) { installer.UseNewContext = true; installer.Path = serviceFilePath; //serviceFilePath是windows服務可執行文件的完整路徑 IDictionary savedState = new Hashtable(); installer.Install(savedState); installer.Commit(savedState); }
卸載windows服務
using (AssemblyInstaller installer = new AssemblyInstaller()) { installer.UseNewContext = true; installer.Path = serviceFilePath; installer.Uninstall(null); }
啟動windows服務
//使用ServiceController.GetServices()可獲取windows服務列表,進而可判斷服務是否存在 //serviceName是注冊的windows服務名稱 using (ServiceController control = new ServiceController(serviceName)) { if (control.Status == ServiceControllerStatus.Stopped) { control.Start(); } }
坑
一切都似乎很簡單,略坑的是,ServiceController.Start方法(注意並不是StartAsync),看起來是一個同步方法,如果服務啟動失敗,按理會異常拋出。而實際情況卻時,Start方法是立即返回的,不會等待服務的啟動結果。方法注釋里發生異常只有兩種情形:
System.ComponentModel.Win32Exception: 訪問系統 API 時出錯。
System.InvalidOperationException: 未找到服務。
至於其它情形導致的啟動失敗(如文件缺失、服務應用程序內部出錯),Start方法一無所知。
ServiceController類有一個Wait方法,作用是阻塞當前線程等待服務到達指定的狀態,還可以設置等待的超時時間,這有一定的用處,但並不理想。當啟動失敗的時候,如何能夠獲取到啟動失敗的信息呢?
一個猥瑣的辦法
windows服務啟動無論成功還是失敗,都會記錄一條windows日志,可以借助對windows日志的監控來實現:
在調用ServiceController.Start方法之前,啟動對windows日志的監聽:
_eventLog = new EventLog("Application"); _eventLog.EnableRaisingEvents = true; _eventLog.EntryWritten += Log_EntryWritten;
在EntryWritten事件處理器里,判斷windows日志類型,進而得知windows服務啟動情況:
private void Log_EntryWritten(object sender, EntryWrittenEventArgs e) { EventLogEntry log = e.Entry; if(log.Source == _currentSection.ServiceName) { if(log.EntryType == EventLogEntryType.Information) { //啟動成功 } else { //啟動失敗 MessageBox.Show(log.Message); } _eventLog.Dispose(); _eventLog = null; } }
這里還一個略坑的點,按一般的事件開發約定,sender參數應該就是發生事件的主體,在這里想來應該就是EventLog類的實例,然而事實上sender是EventLogInternal類的實例,很遺憾的是,這個類並不公開訪問,而且,也並未發現有跟EventLog類的直接關聯。