一、概念名稱 Windows服務(即以前的 NT 服務),使您能夠創建在它們自己的Windows會話中可長時間運行的可執行應用程式。這些服務可以在電腦啟動時自動啟動,可以暫停和重新啟動而且不顯示任何用戶界面。這種服務非常適合在伺服器上使用,或任何時候,為了不影響在同一臺電腦上工作的其他用戶,需要 ...
一、概念名稱
Windows服務(即以前的 NT 服務),使您能夠創建在它們自己的Windows會話中可長時間運行的可執行應用程式。這些服務可以在電腦啟動時自動啟動,可以暫停和重新啟動而且不顯示任何用戶界面。這種服務非常適合在伺服器上使用,或任何時候,為了不影響在同一臺電腦上工作的其他用戶,需要長時間運行功能時使用。
二、創建Windows服務
2.1、創建項目
新建->項目->Windows 桌面->Windows 服務。
項目右鍵屬性->應用程式->輸出類型,可以看出它是屬於"Windows 應用程式"。
2.2、添加安裝程式
打開Service1.cs->空白處右鍵->添加安裝程式。
2.3、設置安裝信息
打開ProjectInstaller.cs。
2.3.1、serviceInstaller1
點擊serviceInstaller1,在屬性中設置服務信息,此示例是創建一個"HelloWorld"的服務。
說明:
Description:服務描述,直接顯示到Windows服務列表中的描述。
DisplayName:服務顯示名稱,直接顯示到Windows服務列表中的名稱。
ServiceName:服務名稱,啟動或停止服務時的標識。
StartType:啟動類型,如自動、手動等。
2.3.2、serviceProcessInstaller1
點擊serviceProcessInstaller1,在屬性中設置運行服務的賬戶類型。
2.4、生成項目
考慮到後面涉及到的Debugger調試方法,此處選擇Release模式進行生成。
三、安裝與卸載服務
3.1、InstallUtil.exe
在VS安裝目錄下將InstallUtil.exe拷貝到項目的Release文件夾下,InstallUtil.exe在VS2017的路徑為:C:\Windows\Microsoft.NET\Framework\v4.0.30319。
3.2、安裝服務
在Release文件夾的地址欄中輸入"cmd"調出命令提示符窗體:
安裝服務命令:
InstallUtil.exe LinkTo.Test.WindowsService.exe
啟動服務命令:
net start HelloWorld
當然,一般我們使用批處理的方式來安裝與卸載服務。
在Release文件夾下麵,創建一個"安裝服務.bat"的批處理文件:
@echo off
echo===================================================
echo LinkTo.Test.WindowsService 正在安裝服務
echo===================================================
@echo off
InstallUtil.exe LinkTo.Test.WindowsService.exe
@echo off
echo===================================================
echo LinkTo.Test.WindowsService 正在啟動服務
echo===================================================
@echo off
net start HelloWorld
pause
在運行中輸入"services.msc"進入服務,即可看到新建的HelloWorld服務:
3.3、卸載服務
在Release文件夾下麵,創建一個"卸載服務.bat"的批處理文件:
@echo off
echo===================================================
echo LinkTo.Test.WindowsService 正在停止服務
echo===================================================
@echo off
net stop HelloWorld
@echo off
echo===================================================
echo LinkTo.Test.WindowsService 正在卸載服務
echo===================================================
@echo off
InstallUtil.exe /u LinkTo.Test.WindowsService.exe
pause
四、服務定時器
一般來說,服務都會設置每隔多長時間執行一次任務,這裡使用System.Threading.Timer來做個簡單的日誌記錄,將日誌寫入到Release\Log文件夾下。
public partial class Service1 : ServiceBase { private static Timer timerAsync = null; private int dueTimeInterval = 1000 * 5; //單位:毫秒 private int periodInterval = 1000 * 5; //單位:毫秒 public Service1() { InitializeComponent(); //callback:一個 TimerCallback 委托,表示要執行的方法。 //state:一個包含回調方法要使用的信息的對象,或者為空引用。 //dueTime:調用 callback 之前延遲的時間量(以毫秒為單位)。指定 Timeout.Infinite 以防止計時器開始計時,指定零(0)以立即啟動計時器。 //period:調用 callback 的時間間隔(以毫秒為單位)。指定 Timeout.Infinite 可以禁用定期終止。 timerAsync = new Timer(AutoAsyncCallback, null, Timeout.Infinite, Timeout.Infinite); } /// <summary> /// 服務啟動 /// </summary> /// <param name="args"></param> protected override void OnStart(string[] args) { base.OnStart(args); timerAsync.Change(dueTimeInterval, periodInterval); WriteLog(DateTime.Now.ToString("HH:mm:ss") + " 服務啟動" + "\r\n"); WriteLog(Environment.NewLine); } /// <summary> /// 服務停止 /// </summary> protected override void OnStop() { base.OnStop(); if (timerAsync != null) { timerAsync.Change(Timeout.Infinite, Timeout.Infinite); timerAsync.Dispose(); timerAsync = null; } WriteLog(DateTime.Now.ToString("HH:mm:ss") + " 服務停止" + "\r\n"); WriteLog(Environment.NewLine); } /// <summary> /// 服務暫停 /// </summary> protected override void OnPause() { base.OnPause(); WriteLog(DateTime.Now.ToString("HH:mm:ss") + " 服務暫停" + "\r\n"); WriteLog(Environment.NewLine); } /// <summary> /// 電腦關閉 /// </summary> protected override void OnShutdown() { base.OnShutdown(); WriteLog(DateTime.Now.ToString("HH:mm:ss") + " 電腦關閉" + "\r\n"); WriteLog(Environment.NewLine); } /// <summary> /// 回調函數 /// </summary> /// <param name="state"></param> private void AutoAsyncCallback(object state) { try { timerAsync.Change(Timeout.Infinite, Timeout.Infinite); #if DEBUG if (!Debugger.IsAttached) Debugger.Launch(); //當進程運行到這裡的時候會自動停下來並彈出提示框 Debugger.Break(); //這個方法和在VS中加入紅色的斷點是一模一樣的 #endif WriteLog(DateTime.Now.ToString("HH:mm:ss") + " AutoAsyncCallback執行開始,線程ID = " + Thread.CurrentThread.ManagedThreadId + "\r\n"); Thread.Sleep(1000 * 10); //模擬耗時較長的計算任務,且耗時大於定時的間隔時間。 } catch (Exception ex) { WriteLog(DateTime.Now.ToString("HH:mm:ss") + " AutoAsyncCallback執行異常:" + "\r\n" + ex.Message); } finally { timerAsync.Change(dueTimeInterval, periodInterval); WriteLog(DateTime.Now.ToString("HH:mm:ss") + " AutoAsyncCallback執行結束" + "\r\n"); WriteLog(Environment.NewLine); } } /// <summary> /// 日誌記錄 /// </summary> /// <param name="logInfo">日誌信息</param> void WriteLog(string logInfo) { try { string logDirectory = AppDomain.CurrentDomain.BaseDirectory + "\\Log"; if (!Directory.Exists(logDirectory)) { Directory.CreateDirectory(logDirectory); } string filePath = logDirectory + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt"; File.AppendAllText(filePath, logInfo); } catch { } } }Service1.cs
五、調試服務
由於Windows服務程式不能直接執行,所以不能直接打斷點進行調試。調試服務的常用方式有以下兩種:
5.1、附加到進程
服務啟動後,點擊調試->附加到進程->選擇LinkTo.Test.WindowsService->附加。
5.2、Debugger
#if DEBUG if (!Debugger.IsAttached) Debugger.Launch(); //當進程運行到這裡的時候會自動停下來並彈出提示框 Debugger.Break(); //這個方法和在VS中加入紅色的斷點是一模一樣的 #endif
使用Debugger代碼進行調試,在項目生成的時候,需使用Release模式,否則一直會有附加提示,可在配置管理器中修改Release模式。