.NET Windows服务需要使用STAThread

发布于 2024-08-16 09:12:39 字数 1570 浏览 5 评论 0 原文

我创建了一个将调用某些 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
    }
}

I have created a Windows Service that will be calling out to some COM components, so I tagged [STAThread] to the Main function. However, when the timer fires, it reports MTA and the COM calls fail. How can I fix this?

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
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

往事随风而去 2024-08-23 09:12:39

服务由 Windows 服务托管系统运行,该系统使用 MTA 线程运行。你无法控制这个。您必须创建一个新的 线程将其 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();
    }
}

请注意,此代码实际上并不停止服务,而是停止计时器。多线程上可能仍有大量工作正在完成。例如,如果您的工作包括在大型数据库上运行多个查询,您可能最终会崩溃,因为同时运行的线程太多。

在这种情况下,我会创建一定数量的 STA 线程(可能是开始时核心数量的 2 倍),用于监视工作项的线程安全队列。计时器滴答事件将负责向该队列加载需要完成的工作。

这一切都取决于您实际上每十秒正在做什么,是否应该在下次计时器计时时完成,在这种情况下您应该做什么等等。

Services are run by the windows service hosting system, which runs using MTA threads. You can't control this. You have to create a new Thread and set its ApartmentState to STA, and do your work on this thread.

Here's a class that extends ServiceBase that does this:

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

Note this code doesn't actually stop the service, it stops the timer. There could be lots of work still being done on multiple threads. For instance, if your work consisted of running multiple queries off a large database you may end up crashing because you have too many threads running at the same time.

In a situation like this, I'd create a set number of STA threads (maybe 2x the number of cores to start off with) which monitor a thread-safe queue for work items. The timer tick event would be responsible for loading that queue with the work needing done.

It all depends on what you're actually doing every ten seconds, whether or not it should be completed the next time the timer ticks, what you should do in this situation, etc etc.

养猫人 2024-08-23 09:12:39

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

That cannot work in a service, the thread that calls your Main() method was already started by the service manager. You'll need to create a separate thread that is initialized with Thread.SetApartmentState() and pumps a message loop.

贵在坚持 2024-08-23 09:12:39

设置 STAThread 属性对服务不起作用。它的处理方式与应用程序不同,因此这将被忽略。

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

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

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

Setting the STAThread attribute will not work on a service. It's not being handled the same way as an application, so this will get ignored.

My recommendation would be to manually make a separate thread for your service, set its apartment state, and move everything into it. This way, you can set the thread to STA correctly.

However, there will be another issue here - you'll have to rework the way your service works. You can't just use a System.Threading.Timer instance for timing - it runs on a separate thread, which will not be STA. When its elapsed event fires, you'll be working on a different, non-STA thread.

Instead of doing your work in the timer event, you'll probably want to do your main work in the thread you create explicitly. You can have a reset event in that thread which blocks, and have your timer "set" it to allow your logic to run in the STA thread.

梦回梦里 2024-08-23 09:12:39

查看类似的示例: http://www.aspfree.com/c/a/C-Sharp/Creating-a-Windows-Service-with-C-Sharp-introduction/1/

如果您的主要是...

    [STAThread]
    public static void Main ()
    {
        MyMonitor m = new MyMonitor();
        m.Start();
    }

并将计时器开始/停止移出事件...

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

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

    protected override void OnStop ()
    {
        EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
    }

Looking at a similar example: http://www.aspfree.com/c/a/C-Sharp/Creating-a-Windows-Service-with-C-Sharp-introduction/1/

What if your main is...

    [STAThread]
    public static void Main ()
    {
        MyMonitor m = new MyMonitor();
        m.Start();
    }

and move your timer start / stop out of the events...

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

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

    protected override void OnStop ()
    {
        EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
    }
甜妞爱困 2024-08-23 09:12:39

该报告表明它正在使用 STA。它基于 Will 的建议和 http://en.csharp -online.net/Creating_a_.NET_Windows_Service%E2%80%94Alternative_1:_Use_a_Separate_Thread

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



namespace MyMonitorService
{
    internal class MyMonitorThreaded : ServiceBase
    {
        private Boolean bServiceStarted = false;
        private Thread threadWorker;

        private void WorkLoop ()
        {
            while (this.bServiceStarted)
            {
                EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);

                if (this.bServiceStarted)
                    Thread.Sleep(new TimeSpan(0, 0, 10));
            }

            Thread.CurrentThread.Abort();
        }

        #region Service Start/Stop
        protected override void OnStart (String[] args)
        {
            this.threadWorker = new Thread(WorkLoop);
            this.threadWorker.SetApartmentState(ApartmentState.STA);
            this.bServiceStarted = true;
            this.threadWorker.Start();
        }

        protected override void OnStop ()
        {
            this.bServiceStarted = false;
            this.threadWorker.Join(new TimeSpan(0, 2, 0));
        }
        #endregion
    }
}

This reports that it is using STA. It is based on Will's suggestion and http://en.csharp-online.net/Creating_a_.NET_Windows_Service%E2%80%94Alternative_1:_Use_a_Separate_Thread

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



namespace MyMonitorService
{
    internal class MyMonitorThreaded : ServiceBase
    {
        private Boolean bServiceStarted = false;
        private Thread threadWorker;

        private void WorkLoop ()
        {
            while (this.bServiceStarted)
            {
                EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);

                if (this.bServiceStarted)
                    Thread.Sleep(new TimeSpan(0, 0, 10));
            }

            Thread.CurrentThread.Abort();
        }

        #region Service Start/Stop
        protected override void OnStart (String[] args)
        {
            this.threadWorker = new Thread(WorkLoop);
            this.threadWorker.SetApartmentState(ApartmentState.STA);
            this.bServiceStarted = true;
            this.threadWorker.Start();
        }

        protected override void OnStop ()
        {
            this.bServiceStarted = false;
            this.threadWorker.Join(new TimeSpan(0, 2, 0));
        }
        #endregion
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文