Windows Service 開發

Windows Service是一種沒有UI,開機後不需使用者登入就會執行的應用程式,通常會用來做一些定時排程的工作。
用C#開發Windows Service很簡單,以下用一個簡單的範例來介紹


首先開一個新專案,名稱為MyService

在方案總管中可以看到,只有Program.cs和Service1.cs兩個檔案

先來看看Program.cs,這是應用程式的進入點,主要是透過ServiceBase.Run來啟動服務
using System.ServiceProcess;

namespace MyService
{
static class Program
{
/// <summary>
/// 應用程式的主要進入點。
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
}
}

再來看一下Service1.cs,可以看到是繼承自ServiceBase的類別,再自行覆寫要處理的事件
using System.ServiceProcess;

namespace MyService
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
}

protected override void OnStop()
{
}
}
}

可覆寫的方法如下
OnContinue指定暫停服務後要繼續正常運作所要執行的動作
OnCustomCommand指定在具有指定參數值的命令發生時所要執行的動作
需要注意命令編號為128~256
OnPause指定在服務暫停時所要執行的動作
OnPowerEvent這適用於攜帶型電腦,當它們進入暫停模式的時候,不同於系統關閉
OnSessionChange當從 Terminal Server 工作階段接收到變更事件時執行
OnShutdown指定緊接在系統關閉之前應該發生的處理
OnStart指定在服務啟動時所要執行的動作
OnStop指定在服務停止執行時所要執行的動作

可設定的屬性如下,最常用的還是ServiceName這個屬性

更詳細的內容可以參考MSDN關於ServiceBase.aspx)的介紹

先來簡單地寫個定時功能
透過Log元件輸出目前時間到Log2Console
相關內容可以參考之前的筆記
Common.Logging
好用的LogViewer

using System;
using System.ServiceProcess;
using System.Timers;
using Common.Logging;

namespace MyService
{
public partial class Service1 : ServiceBase
{
private Timer timer;
private ILog log;
private string datetimeFormat = “yyyy/MM/dd HH:mm:ss”;
private double timerInterval = 1000;

public Service1()
{
InitializeComponent();
this.log = LogManager.GetLogger(typeof(Service1));
this.timer = new Timer();
this.timer.Interval = this.timerInterval;
this.timer.AutoReset = false;
this.timer.Enabled = false;
this.timer.Elapsed += Timer_Elapsed;
}

protected override void OnStart(string[] args)
{
this.timer.Enabled = true;
}

protected override void OnStop()
{
this.timer.Enabled = false;
}

private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
this.timer.Stop();

try
{
this.log.TraceFormat(“now:{0}”, DateTime.Now.ToString(this.datetimeFormat));
}
catch (Exception ex)
{
this.log.Error(ex);
}

this.timer.Start();
}
}
}

接下來要增加安裝程式,在Service1.cs的設計畫面按右鍵,選擇加入安裝程式

會增加一個繼承自System.Configuration.Install.Installer的ProjectInstaller類別

先設定服務執行時使用的帳號,比較安全的帳號是NetworkService,如果有需要存取資源的話,再去允許權限或是改用LocalSystem這個帳號

接下來設定服務的啟動方式,最常用的當然是自動啟動

DisplayName是名稱,Description描述

設定好後就完成這個Service,編譯後產生執行檔,接下來就要把這個服務安裝的到作業系統中
比較正規的作法是去新增一個安裝專案,但為了方便開發起見,以下用批次檔的方式來安裝

新增幾個批次檔,並改成永遠複製

install.bat用來安裝
@echo off
set InstallUtil=%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe
if not exist %InstallUtil% set InstallUtil=%WINDIR%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe
if not exist %InstallUtil% set InstallUtil=%WINDIR%\Microsoft.NET\Framework\v3.5\InstallUtil.exe
if not exist %InstallUtil% (
echo InstallUtil.exe not found
exit
)

%InstallUtil% -i MyService.exe

restart.bat用來重新啟動服務
net stop Service1
net start Service1

start.bat用來啟動服務
net start Service1

stop.bat用來停止服務
net stop Service1

uninstall.bat用來反安裝
@echo off
set InstallUtil=%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe
if not exist %InstallUtil% set InstallUtil=%WINDIR%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe
if not exist %InstallUtil% set InstallUtil=%WINDIR%\Microsoft.NET\Framework\v3.5\InstallUtil.exe
if not exist %InstallUtil% (
echo InstallUtil.exe not found
exit
)

%InstallUtil% -u MyService.exe

注意檔案編碼為ANSI格式,UTF8格式在command pro中執行會有亂碼
批次檔是透過InstallUtil.exe這個工具程式來安裝和反安裝,詳細的參數請參考MSDN的說明.aspx)

點擊install.bat,安裝成功

點繫start.bat,啟動服務

順利的話就可以在Log2Console中定時收到目前時間

要移除服務就點擊uninstall.bat,反安裝成功