我正在创建一个自动测试运行应用程序。在这部分应用程序中,我正在研究一个轮询服务器。它的工作原理是不断轮询Web服务器以确定何时应运行新的自动化测试(对于我们的GUI应用程序的夜间自动运行)。
当轮询服务器看到请求时,它会下载所需的所有信息,然后在后台工作程序中执行测试运行。问题是测试运行的一部分有后台工作线程中出现的OLE,COM和其他调用(例如,Clipboard.Clear()
)。当其中一个调用发生时,会发生以下异常:
在进行OLE调用之前,必须将当前线程设置为单线程单元(STA)模式。确保您的Main函数标记了STAThreadAttribute。
如何将后台工作线程标记为单线程单元?我的Program.cs中的Main调用显然已经具有该属性。
答案 0 :(得分:36)
这是不可能的,BGW使用线程池线程。 TP线程始终是MTA,不能更改。您必须使用常规Thread,在启动之前调用SetApartmentState()。这个线程也应该引发一个消息循环,调用Application.Run()。
也许您应该考虑从UI线程调用此代码。因为很可能COM服务器无论如何都在UI线程上运行它的方法。从工作线程到创建COM服务器的STA线程的Marshaling调用是自动的,COM负责处理它。
或者采取公牛角,并自己编组。您可以创建自己的STA线程,为服务器提供一个幸福的家。您将在this post中找到代码,请确保在Initialize()覆盖中创建COM对象。
答案 1 :(得分:8)
BackgroundWorker默认使用ThreadPool线程,但您可以覆盖此行为。首先,您需要定义自定义SynchronizationContext:
public class MySynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
Thread t = new Thread(d.Invoke);
t.SetApartmentState(ApartmentState.STA);
t.Start(state);
}
}
在使用BackgroundWorker之前覆盖默认的SynchronizationContext,如下所示:
AsyncOperationManager.SynchronizationContext = new MySynchronizationContext();
注意:这会对应用程序的其余部分产生性能影响,因此您可能希望限制新的Post实现(例如使用 state 或 d 参数)。
答案 2 :(得分:4)
我还没有测试过,但是如果你调用WinForms表单,你应该回到UI线程,大部分内容都应该再次运行。
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(this.bgw_DoWork);
bgw.RunWorkerAsync();
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
// Invoke the UI thread
// "this" is referring to the Form1, or what ever your form is
this.Invoke((MethodInvoker)delegate
{
Clipboard.GetText();
// etc etc
});
}
答案 3 :(得分:1)
您通常是通过在入口点定义attributre [STAThread()]
来设置它(例如Static Main)。
答案 4 :(得分:-1)
我使用了+ Conrad de Wet的想法并且效果很好!
但该代码存在一个小问题,你必须关闭“this.Invoke .....”,如同}};
以下是Conrad de Wet的修复代码:
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(this.bgw_DoWork);
bgw.RunWorkerAsync();>
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
// Invoke the UI thread
// "this" is referring to the Form1, or what ever your form is
this.Invoke((MethodInvoker)delegate
{
Clipboard.GetText();
// etc etc
});
}