2011年8月19日

[.Net] C# Express 撰寫 Windows Service 的方式

手工打造 Windows Service

Step 1.新增專案
C# Express裡專案範本只有這幾個,不過沒關系,
先選主控台應用程式。
image

Step 2.在方案總管加入兩個新的類別
imageimage

第一個類別,負責當服務啟動及停止時要做的事情
先加入 System.ServiceProcess 的參考
image

image
繼承 ServiceBase,並複寫 OnStart 及 OnStop
另外在建構式中 ,要初始化相關服務的資訊,
這個部分是Windows Service啟動時所需要的。



MyWinService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ServiceProcess;
using System.Diagnostics;
using System.Timers;

namespace MyWinService
{
    class MyWinService : ServiceBase
    {
        const string SERVICE_NAME = @"MyWinService";
        System.Timers.Timer TheTimer = new System.Timers.Timer();

        public MyWinService()
        {
            InitializeComponent();
        }
        private void InitializeComponent()
        {
            this.ServiceName = SERVICE_NAME;
            this.CanStop = true;
            this.AutoLog = false;
            this.EventLog.Log = "Application";
            this.EventLog.Source = SERVICE_NAME;
        }
        protected override void OnStart(string[] args)
        {
            //Todo:  config 取得 Interval
            TheTimer.Interval = 30 * 1000;
            TheTimer.Elapsed += new ElapsedEventHandler(TheTimer_Elapsed);
            TheTimer.Start();
            this.EventLog.WriteEntry(SERVICE_NAME + " Service has started.");
        }
        protected override void OnStop()
        {
            TheTimer.Stop();
            this.EventLog.WriteEntry(SERVICE_NAME + " Service has stopped.");
        }
        protected void TheTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            //Todo: your code here
        }
    }
}
第二個類別,服務安裝程式會呼叫到的類別,負責將安裝服務
加入 System.Configuration.Install 的參考
image

MyWinServiceInstaller.cs
using System;
using System.ServiceProcess;
using System.ComponentModel;
using System.Configuration.Install;

namespace MyWinService
{
    [RunInstaller(true)]
    public class MyWinServiceInstaller : Installer
    {
        const string SERVICE_NAME = @"MyWinService";
        private ServiceProcessInstaller ServiceProcessInstaller1;

        private ServiceInstaller ServiceInstaller1;
        public MyWinServiceInstaller()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.ServiceProcessInstaller1 = new ServiceProcessInstaller();
            this.ServiceProcessInstaller1.Account = ServiceAccount.LocalSystem; //服務啟動預設登入類型(安全性)
            this.ServiceProcessInstaller1.Username = null;
            this.ServiceProcessInstaller1.Password = null;
            this.ServiceInstaller1 = new ServiceInstaller();
            this.ServiceInstaller1.Description = "這裡寫服務的描述";
            this.ServiceInstaller1.DisplayName = SERVICE_NAME;
            this.ServiceInstaller1.ServiceName = SERVICE_NAME;
            this.ServiceInstaller1.StartType = ServiceStartMode.Automatic; //啟動模式(自動/手動/停用)
            this.Installers.AddRange(new Installer[] { this.ServiceProcessInstaller1, this.ServiceInstaller1 });

            this.Committed += new InstallEventHandler(MyWinServiceInstaller_Committed); //當安裝成功後,啟動服務
        }
        void MyWinServiceInstaller_Committed(object sender, InstallEventArgs e)
        {
            // Auto Start the Service Once Installation is Finished.
            var controller = new ServiceController(SERVICE_NAME);
            controller.Start();
        }
    }
}
Step 3.主程式
這個部分是負責服務啟動時,載入 MyWinServices 類別
另外安裝及解除安裝也一併在這裡處理,方便設定服務,而不需要另外包裝安裝程式。

Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.ServiceProcess;
using System.Runtime.InteropServices;

namespace MyWinService
{
    class Program
    {
        /// <summary>
        /// 主程式
        /// </summary>
        /// <param name="args"></param>
        [STAThread()]
        static void Main(string[] args)
        {
            //沒有輸入參數的時候,當做起動服務的入口(若啟動服務也需要參數的話,這邊要改寫)
            if (args.Length == 0) {
                System.ServiceProcess.ServiceBase.Run(new MyWinService());
            } else {
                if (args[0].ToUpper() == "/INSTALL")  {
                    //安裝
                    InstallService();
                } else if (args[0].ToUpper() == "/UNINSTALL") {
                    //解除安裝
                    UnInstallService();
                } else {
                    Console.Write("輸入的參數不正確!");
                }
            }
        }
        static string INSTALL_EXE = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory()
                                                , @"InstallUtil.exe");
        static void InstallService()
        {
            string currFileName = Process.GetCurrentProcess().MainModule.FileName;
            Process.Start(INSTALL_EXE, string.Format(@"""{0}""", currFileName));
        }
        static void UnInstallService()
        {
            string currFileName = Process.GetCurrentProcess().MainModule.FileName;
            Process.Start(INSTALL_EXE, string.Format(@"/u ""{0}""", currFileName));
        }
    }
}
這樣就大功告成囉!
以下是執行的畫面:
直接執行不給參數會要求我們要安裝服務
image

使用 /install 安裝服務:
image

服務安裝成功並啟動:
image

參考資訊:http://social.msdn.microsoft.com/Forums/en/Vsexpressvcs/thread/8b9207c8-4b59-4740-b6c3-c5fb1d8fea56