2014年1月20日 星期一

Windows Service 本機偵錯方式

Windows Service輸出類型雖然是Windows 應用程式

但開發的時後直接執行卻會出現啟動錯誤的提示訊息

原因在於程式進入點是用ServiceBase來啟動,而不是Application.Run
using System.ServiceProcess;

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

為了方便本機開發,就需要先動點手腳
首先先把邏輯拆到獨立的類別去,並公開Start和Stop方法
using System;
using System.Timers;
using Common.Logging;

namespace MyService
{
    internal class NowTimeReporter
    {
        private Timer timer;
        private ILog log;
        private double timerInterval = 1000;
        private string datetimeFormat = "yyyy/MM/dd HH:mm:ss";

        public NowTimeReporter()
        {
            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;
        }

        public void Start()
        {
            this.timer.Enabled = true;
        }

        public void Stop()
        {
            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();
        }
    }
}


讓Service只是簡單的呼叫Start和Stop
using System.ServiceProcess;

namespace MyService
{
    public partial class Service1 : ServiceBase
    {
        private NowTimeReporter reporter = new NowTimeReporter();

        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            this.reporter.Start();
        }

        protected override void OnStop()
        {
            this.reporter.Stop();
        }
    }
}

把專案類型切換成主控台應用程式


接下來在程式進入點的地方利用執行時的使用者名稱來判斷要用那一種方式啟動
using System;
using System.ServiceProcess;

namespace MyService
{
    static class Program
    {
        /// <summary>
        /// 應用程式的主要進入點。
        /// </summary>
        static void Main()
        {
            if (Environment.UserName == "SYSTEM")
            {
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[] 
                { 
                    new Service1() 
                };
                ServiceBase.Run(ServicesToRun);
            }
            else
            {
                NowTimeReporter reporter = new NowTimeReporter();
                reporter.Start();
                Console.ReadLine();
            }
        }
    }
}

然後在本機開發的時後,就可以直接下中斷點了