27

我正在创建一个自动化测试运行应用程序。在应用程序的这一部分,我正在使用轮询服务器。它通过不断地轮询 Web 服务器来确定何时应该运行新的自动化测试(对于我们的 GUI 应用程序的夜间自动化运行)。

当轮询服务器看到一个请求时,它会下载所有必要的信息,然后在后台工作程序中执行测试运行。Clipboard.Clear()问题是测试运行的一部分具有在后台工作线程中发生的OLE、COM 和其他调用(例如, )。当其中一个调用发生时,会发生以下异常:

在进行 OLE 调用之前,必须将当前线程设置为单线程单元 (STA) 模式。确保您的 Main 函数上标记了 STAThreadAttribute。

如何将后台工作线程标记为单线程单元?我的 Program.cs 中的 Main 调用显然已经具有该属性。

4

5 回答 5

36

这是不可能的,BGW 使用线程池线程。TP 线程始终是 MTA,无法更改。您必须使用常规线程,在启动之前调用 SetApartmentState()。该线程还应该泵送一个消息循环,调用 Application.Run()。

也许您应该考虑从 UI 线程调用此代码。因为很可能,COM 服务器无论如何都在 UI 线程上运行它的方法。从工作线程到创建 COM 服务器的 STA 线程的编组调用是自动的,由 COM 负责。

或者采取公牛的角并自己编组。您可以创建自己的 STA 线程,给服务器一个快乐的家。您将在这篇文章中找到代码,请务必在 Initialize() 覆盖中创建 COM 对象。

于 2011-01-13T21:09:52.263 回答
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 实现(例如使用stated参数)。

于 2013-02-14T09:43:00.847 回答
7

我还没有测试过,但是如果你调用 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
    });
}
于 2013-08-12T12:07:13.210 回答
1

[STAThread()]您通常通过在入口点(例如静态主)上定义属性来设置它。

于 2011-01-13T21:09:50.003 回答
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
        });
    }
于 2014-07-08T17:30:29.637 回答