我创建了一个将调用某些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
}
}
答案 0 :(得分:24)
服务由Windows服务托管系统运行,该系统使用MTA线程运行。你无法控制它。您必须创建一个新的Thread和set its ApartmentState to 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倍),它监视工作项的线程安全队列。计时器tick事件将负责加载该队列需要完成的工作。
这一切都取决于你每十秒钟实际做了什么,是否应该在下次计时器滴答时完成,在这种情况下你应该做什么等等。
答案 1 :(得分:5)
设置STAThread属性不适用于服务。它的处理方式与应用程序不同,因此会被忽略。
我的建议是为您的服务手动创建一个单独的线程,设置其公寓状态,并将所有内容移入其中。这样,您可以正确地将线程设置为STA。
但是,这里还会有另一个问题 - 您必须重新设计服务的工作方式。您不能只使用System.Threading.Timer实例进行计时 - 它在一个单独的线程上运行,该线程不是STA。当其经过的事件触发时,您将处理另一个非STA线程。
您可能希望在明确创建的线程中执行主要工作,而不是在计时器事件中执行您的工作。你可以在该线程中有一个重置事件阻塞,并让你的定时器“设置”它以允许你的逻辑在STA线程中运行。
答案 2 :(得分:4)
这在服务中无效,调用Main()方法的线程已由服务管理器启动。您需要创建一个使用Thread.SetApartmentState()初始化的单独线程并泵送消息循环。
答案 3 :(得分:0)
看一个类似的例子: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);
}
答案 4 :(得分:0)
报告它正在使用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
}
}