.NET Windows服务需要使用STAThread

Wal*_*ams 14 .net windows-services sta

我创建了一个将调用某些COM组件的Windows服务,因此我将[STAThread]标记为Main函数.但是,当计时器触发时,它会报告MTA并且COM调用失败.我怎样才能解决这个问题?

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



namespace MyMonitorService
{
    public class MyMonitor : ServiceBase
    {
        #region Members
        private System.Timers.Timer timer = new System.Timers.Timer();
        #endregion

        #region Construction
        public MyMonitor ()
        {
            this.timer.Interval = 10000; // set for 10 seconds
            this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.timer_Elapsed);
        }
        #endregion

        private void timer_Elapsed (object sender, ElapsedEventArgs e)
        {
            EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);
        }

        #region Service Start/Stop
        [STAThread]
        public static void Main ()
        {
            ServiceBase.Run(new MyMonitor());
        }

        protected override void OnStart (string[] args)
        {
            EventLog.WriteEntry("MyMonitor", "My Monitor Service Started", EventLogEntryType.Information);
            this.timer.Enabled = true;
        }

        protected override void OnStop ()
        {
            EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
            this.timer.Enabled = false;
        }
        #endregion
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 25

服务由Windows服务托管系统运行,该系统使用MTA线程运行.你无法控制它.您必须创建一个新的Thread并将其ApartmentState设置为STA,并在此线程上完成您的工作.

这是一个扩展ServiceBase的类,它执行此操作:

public partial class Service1 : ServiceBase
{
    private System.Timers.Timer timer;

    public Service1()
    {
        InitializeComponent();
        timer = new System.Timers.Timer();
        this.timer.Interval = 10000; // set for 10 seconds
        this.timer.Elapsed += new System.Timers.ElapsedEventHandler(Tick);
    }

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

    private void Tick(object sender, ElapsedEventArgs e)
    {
        // create a thread, give it the worker, let it go
        // is collected when done (not IDisposable)
        var thread = new Thread(WorkerMethod);
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        OnStop(); // kill the timer
    }

    private void WorkerMethod(object state)
    {
        // do your work here in an STA thread
    }

    protected override void OnStop()
    {
        timer.Stop();
        timer.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,此代码实际上不会停止服务,它会停止计时器.在多个线程上可能还有很多工作要做.例如,如果您的工作包括从大型数据库运行多个查询,则最终可能会崩溃,因为您有太多的线程同时运行.

在这种情况下,我将创建一组STA线程(可能是启动的核心数量的2倍),它监视工作项的线程安全队列.计时器tick事件将负责加载该队列需要完成的工作.

这一切都取决于你每十秒钟实际做了什么,是否应该在下次计时器滴答时完成,在这种情况下你应该做什么等等.


Han*_*ant 5

这不能在服务中工作,调用您的 Main() 方法的线程已经由服务管理器启动。您需要创建一个单独的线程,该线程使用 Thread.SetApartmentState() 进行初始化并生成一个消息循环。


Ree*_*sey 5

设置STAThread属性不适用于服务.它的处理方式与应用程序不同,因此会被忽略.

我的建议是为您的服务手动创建一个单独的线程,设置其公寓状态,并将所有内容移入其中.这样,您可以正确地将线程设置为STA.

但是,这里还会有另一个问题 - 您必须重新设计服务的工作方式.您不能只使用System.Threading.Timer实例进行计时 - 它在一个单独的线程上运行,该线程不是STA.当它的已发生事件触发时,您将处理另一个非STA线程.

您可能希望在显式创建的线程中完成主要工作,而不是在计时器事件中执行您的工作.你可以在该线程中有一个重置事件阻塞,并让你的定时器"设置"它以允许你的逻辑在STA线程中运行.