带计时器的 Windows 服务

Posted

技术标签:

【中文标题】带计时器的 Windows 服务【英文标题】:Windows service with timer 【发布时间】:2012-10-04 19:12:54 【问题描述】:

我在 c#.net 中创建了一个带有计时器的 Windows 服务。当我在 Visual Studio 中调试/构建项目时它工作正常,但安装后它不执行操作。

这背后的原因可能是什么?

代码:

public partial class Service1 : ServiceBase

        FileStream fs;
        StreamWriter m_streamWriter;
        Timer tm = new Timer();

        public Service1()
        
            InitializeComponent();

            this.ServiceName = "timerservice";

            tm.Interval = 2000;
            tm.Tick += new EventHandler(PerformOperations);
            tm.Start();

            fs = new FileStream(@"c:\mcWindowsService.txt", FileMode.OpenOrCreate, FileAccess.Write);

            m_streamWriter = new StreamWriter(fs);
            m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
        

        private void PerformOperations(object sener, EventArgs e)
        
            //StreamWriter swr = new StreamWriter("c:\\test_from_database.txt",true);

            try
            
                OdbcConnection con = new OdbcConnection("DSN=liquor_data");

                OdbcDataAdapter adp = new OdbcDataAdapter("", con);

                DataSet ds = new DataSet();

                string sql = "select * from item_group";
                adp.SelectCommand.CommandText = sql;

                adp.Fill(ds, "item_group");

                foreach (DataRow dr in ds.Tables["item_group"].Rows)
                
                    //      swr.Write(dr["group_name"].ToString() + "\t\t" + DateTime.Now.TimeOfDay.ToString() + "\n");

                    //Console.WriteLine(dr["group_name"].ToString() + "\t\t" + DateTime.Now.TimeOfDay.ToString() + "\n");
                    m_streamWriter.WriteLine(dr["group_name"].ToString() + "\t\t" + DateTime.Now.TimeOfDay.ToString() + "\n");
                

                m_streamWriter.Flush();
            

            catch (Exception ex)
            
                // swr.Write("Error :"+ ex.Message + "\t\t" + DateTime.Now.TimeOfDay.ToString() + "\n"); 
            
        
    

【问题讨论】:

账号安装后是否有权限运行windows服务? 尝试取消注释 catch 以查看是否抛出了一些错误。还要检查 Windows 事件日志是否有任何错误。 很难相信这适用于您的开发机器。您正在使用的 Timer 类需要一个消息循环。请改用 System.Threading.Timer。 如果你想确保事件不会被非后台线程欺负,或者悄悄地丢失(计时器在历史上一直存在在 win svc 中永远丢失事件的问题),你不能完全使用计时器。 【参考方案1】:

Windows 服务的第一种方法并不容易..

很久以前,我写了一个C#服务。

这是Service类的逻辑(经过测试,工作正常):

namespace MyServiceApp

    public class MyService : ServiceBase
    
        private System.Timers.Timer timer;

        protected override void OnStart(string[] args)
        
            this.timer = new System.Timers.Timer(30000D);  // 30000 milliseconds = 30 seconds
            this.timer.AutoReset = true;
            this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.timer_Elapsed);
            this.timer.Start();
        

        protected override void OnStop()
        
            this.timer.Stop();
            this.timer = null;
        

        private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        
            MyServiceApp.ServiceWork.Main(); // my separate static method for do work
        

        public MyService()
        
            this.ServiceName = "MyService";
        

        // service entry point
        static void Main()
        
            System.ServiceProcess.ServiceBase.Run(new MyService());
        
    

我建议您在单独的静态方法中编写您的真实服务工作(为什么不,在控制台应用程序中......只需添加对它的引用),以简化调试和清理服务代码。

确保间隔足够,并且只在 OnStart 和 OnStop 覆盖中写入日志。

希望这会有所帮助!

【讨论】:

"并且只在 OnStart 和 OnStop 覆盖中写入日志。"为什么?您不能在每个计时器经过的事件中写入文本文件吗?我遇到了这个问题......我有自己的函数可以写入文本文件(日志),但它不起作用(仅适用于启动和停止),你能帮我理解问题所在吗? 是的,你也可以写入timer_tick方法,但是对我来说是不必要的(主要工作方法必须写入它的日志)。你能告诉我更多(例外等)吗? 绝对运作良好。按照时间间隔继续工作。谢谢!【参考方案2】:

您需要将主代码放在OnStart 方法上。

我的另一个SO answer 可能会有所帮助。

您需要添加一些代码以在visual-studio 中启用调试,同时保持您的应用程序作为Windows 服务有效。另一个SO thread 涵盖了调试 Windows 服务的问题。

编辑

另请参阅 here 的可用文档,了解 MSDN 上的 OnStart 方法,您可以在其中阅读:

不要使用构造函数来执行应该在 开机。使用 OnStart 处理服务的所有初始化。这 构造函数在应用程序的可执行文件运行时调用,而不是在 服务运行。可执行文件在 OnStart 之前运行。当你 继续,例如,构造函数不会再次调用,因为 SCM 已经将对象保存在内存中。如果 OnStop 释放资源 在构造函数中而不是在 OnStart 中分配,需要 第二次服务时不会再次创建资源 调用。

【讨论】:

【参考方案3】:

这是一个工作示例,其中服务的执行是在 Timer 的 OnTimedEvent 中开始的,它在 ServiceBase 类中作为委托实现,并且 Timer 逻辑封装在一个名为 SetupProcessingTimer() 的方法中:

public partial class MyServiceProject: ServiceBase


private Timer _timer;

public MyServiceProject()

    InitializeComponent();


private void SetupProcessingTimer()

    _timer = new Timer();
    _timer.AutoReset = true;
    double interval = Settings.Default.Interval;
    _timer.Interval = interval * 60000;
    _timer.Enabled = true;
    _timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);


private void OnTimedEvent(object source, ElapsedEventArgs e)

    // begin your service work
    MakeSomething();


protected override void OnStart(string[] args)

    SetupProcessingTimer();


...

间隔在 app.config 中以分钟为单位定义:

<userSettings>
    <MyProject.Properties.Settings>
        <setting name="Interval" serializeAs="String">
            <value>1</value>
        </setting>
    </MyProject.Properties.Settings>
</userSettings>

【讨论】:

以上是关于带计时器的 Windows 服务的主要内容,如果未能解决你的问题,请参考以下文章

在 Windows 服务中使用的最佳计时器

如何知道是不是从 Windows 本地服务启用了唤醒计时器?

在 Windows 服务中从事件处理程序启动计时器

C# Windows 服务 - 我的计时器不工作,获得多个线程

在 Windows 服务上调用公共方法

Windows 服务中的计时器 .. 这个规模可以吗?会不会出现线程问题?这是不好的做法吗?