[.Net / C#] 撰寫在背景執行的服務 (Windows Service)

最近在研究Windows Service

網路資料有些片段,寫個文章整理一下

 

主要目標很簡單,就是寫一個排程器

每隔幾秒做事情

放在伺服器上

—–

以下用visual studio 2012做示範

 

建立專案

一樣老樣子從開新專案開始

File -> New -> Project…

Screen Shot 2014-02-03 at 7.09.57 PM  

選Visual C# -> Windows -> Windows Service   (中文叫做Windows服務)

然後打上專案名稱 (這裡用MyService1)

按下OK後建立

Screen Shot 2014-02-03 at 7.10.18 PM  

然後就建立專案

Screen Shot 2014-02-03 at 7.51.03 PM  

這裡可以發現這個Service的專案跟一般視窗專案有些不同

除了一樣有Program.cs

多了Service1.cs

點選Service1.cs在Design模式時,什麼都沒有(因為只有元件沒有畫面)

點選Click here to switch to code view直接看程式碼

Screen Shot 2014-02-03 at 7.11.28 PM  

這裡就有原始碼

Screen Shot 2014-02-03 at 7.55.37 PM    

只有很簡單的OnStart跟OnStop

因為它是服務,所以無法直接執行

直接按Start按鈕執行的話會看到這個視窗

Screen Shot 2014-02-03 at 7.12.25 PM  

意思是說,這服務需要被安裝才可以執行

需要用到 installutil.exe 這程式來安裝(等下再介紹)
然後還要用NET START指令啟動服務(這可以不用理會沒關係,我們可以用圖形介面)

———

到這裡還沒有開始寫排程器,還要爲這個服務做一些設定

回到Service1.cs的Design模式(就是剛剛什麼都沒有地方)

按右鍵 -> Add Installer (加入安裝程式)

Screen Shot 2014-02-03 at 8.01.11 PM  

這時會多一個ProjectInstaller.cs檔案,在設計模式底下會有這二個東西

Screen Shot 2014-02-03 at 8.05.49 PM  

點選 serviceInstaller1

Screen Shot 2014-02-03 at 8.08.00 PM  

這裡需要做一些設定

DisplayName 就是服務要顯示的名稱

Description 服務描述

ServiceName 服務的唯一名稱

StartType 啓動方式,初始值爲Manual (手動)
當然要設定Automatic (自動) 呀

DelayedAutoStart 爲開機之後是否延遲啓動

 

點選 serviceProcessInstaller1

Screen Shot 2014-02-03 at 8.13.38 PM  

設定只要改一個

Account 這裡決定服務的帳號與權限

我們可以設定爲LocalSystem (就是最大權限)

———

 

撰寫相關程式碼

先撰寫定時器

這裡用到 System.Timers 所以要先引用

using System.Timers;

 

然後寫一個定時器

private Timer MyTimer;

public Service1()

{

    InitializeComponent();

}

 

protected override void OnStart(string[] args)

{

    MyTimer = new Timer();

    MyTimer.Elapsed += new ElapsedEventHandler(MyTimer_Elapsed);

    MyTimer.Interval = 10 * 1000;

    MyTimer.Start();

}

 

private void MyTimer_Elapsed(object sender, ElapsedEventArgs e)

{

    // Do SomeThing…

}

 

這裡設定 定時器為每10秒 執行一次

 

日誌輸出

在設計模式拉一個EventLog

Screen Shot 2014-02-03 at 8.55.57 PM  

然後增加一些程式碼變成如下

private Timer MyTimer;

 

public Service1()

{

    InitializeComponent();

    this.AutoLog = false;

    if (!System.Diagnostics.EventLog.SourceExists(“MySource”))

    {

        System.Diagnostics.EventLog.CreateEventSource(

            “MySource”, “MyLog”);

    }

    eventLog1.Source = “MySource”;

}

 

protected override void OnStart(string[] args)

{

    eventLog1.WriteEntry(“Start Timer.”);

    MyTimer = new Timer();

    MyTimer.Elapsed += new ElapsedEventHandler(MyTimer_Elapsed);

    MyTimer.Interval = 10 * 1000;

    MyTimer.Start();

}

 

private void MyTimer_Elapsed(object sender, ElapsedEventArgs e)

{

    eventLog1.WriteEntry(“Timer Ticked.”);

}

 

protected override void OnStop()

{

    eventLog1.WriteEntry(“Stop Timer.”);

    MyTimer.Stop();

    MyTimer = null;

}

 

 

剩下就讓大家自由發揮了

——————————

安裝 / 解除安裝 服務

 

剛剛不是有提到 installutil.exe 嗎?

官方文件是寫

安裝服務的時候執行指令

InstallUtil MyService1.exe

解除安裝的時候執行指令

InstallUtil /u MyService1.exe

但事情沒有這麼簡單

 

installutil.exe 它其實是在

C:WindowsMicrosoft.NETFramework<版本號>   底下

Screen Shot 2014-02-03 at 7.13.05 PM  

版本號會有很多個

因為我這個專案是用.Net Framework 4.5

所以就用v4.0.30319

有安裝.Net Framework 4.5都會有這個資料夾,也會有這個程式

但還有一個問題

因為我執行時是用LocalSystem

這個需要額外的管理員權限

——-

在這裡我寫了二個Bat檔案,做安裝服務與解除安裝的服務

這裡只列安裝服務,反正把指令加上 /u 就是 解除安裝就不列出了

Install-MyService1.bat

內容為 

@ECHO OFF

net session >nul 2>&1
IF NOT %ERRORLEVEL% EQU 0 (
   ECHO ERROR: Please run Bat as Administrator.
   PAUSE
   EXIT /B 1
)

@SETLOCAL enableextensions
@CD /d “%~dp0”

REM The following directory is for .NET 4
SET DOTNETFX4=%SystemRoot%Microsoft.NETFrameworkv4.0.30319
SET PATH=%PATH%;%DOTNETFX4%

ECHO Installing MyService1…
ECHO —————————————————
InstallUtil /i .MyService1.exe
ECHO —————————————————
ECHO Done.
PAUSE

 

藍色字自行修改所需要的.Net版本號
還有服務程式的exe名稱

橘色字是檢查管理員權限用的
若Account不是選LocalSystem的話

可以刪掉它

——————————

執行測試

完成之後可以做安裝,然後做測試

在Install-MyService1.bat按右鍵 -> Run as administrator

Screen Shot 2014-02-03 at 9.16.09 PM   

打開控制台 -> 系統管理工具 -> 服務

這個列表裡面就會多了一個你的服務

Screen Shot 2014-02-03 at 9.24.25 PM

裡面的名稱都跟剛剛打的一樣

 Screen Shot 2014-02-03 at 9.24.41 PM  

 

那我們剛剛寫的日誌呢?

控制台 -> 系統管理工具 -> 事件檢視器

找到Applications and Services (應用程式及服務記錄檔)

就會多出一個MyLog

裡面就是我們寫的日誌檔

Screen Shot 2014-02-03 at 9.29.35 PM  

 

除錯

之前在重複若干次的 安裝服務/解除安裝服務 的時候

有遇到EventLog的問題

可以嘗試去登錄編輯器(regedit)刪除以下機碼

HKEY_LOCAL_MACHINESYSTEMControlSet001serviceseventlogMySource
HKEY_LOCAL_MACHINESYSTEMControlSet001servicesMySource
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetserviceseventlogMySource
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetservicesMySource

在執行 解除安裝服務 的程式

 

參考資料:

http://blog.wahahajk.com/2008/06/cservice.html
http://msdn.microsoft.com/en-us/library/y817hyb6(v=vs.110).aspx
http://gogo1119.pixnet.net/blog/post/27575780-%5Bc%23%5D-windows-service%E5%BB%BA%E7%AB%8B%E7%AF%84%E4%BE%8B
http://www.cnblogs.com/xianspace/archive/2009/04/05/1429835.html

http://stackoverflow.com/questions/3307151/receiving-has-already-been-registered-from-eventlog-createeventsource-ev
http://stackoverflow.com/questions/4824051/problem-installing-windows-service

 

在〈[.Net / C#] 撰寫在背景執行的服務 (Windows Service)〉中有 8 則留言

  1. 謝謝!您幫了我大忙!
    在 install service 的時候彈出「設定服務登入」的訊息視窗,這讓我很苦惱。
    因為我拿「設定服務登入」去查 Google,資訊相當有限。
    還好有您的分享,謝謝!

  2. 不好意思,請問安裝BAT檔中
    InstallUtil /i .\ … 中的 /i 作用為何?

留言功能已關閉。