web-dev-qa-db-ja.com

バックグラウンドワーカースレッドをシングルスレッドアパートメントに設定するにはどうすればよいですか?

自動テスト実行アプリケーションを作成しています。アプリケーションのこの部分では、ポーリングサーバーで作業しています。これは、Webサーバーを常にポーリングして、新しい自動テストをいつ実行するかを決定することによって機能します(GUIアプリケーションの夜間自動実行の場合)。

ポーリングサーバーが要求を確認すると、必要なすべての情報をダウンロードし、バックグラウンドワーカーでテスト実行を実行します。問題は、テスト実行の一部にOLE、COM、およびバックグラウンドワーカースレッドで発生するその他の呼び出し(たとえば、Clipboard.Clear())があることです。これらの呼び出しの1つが発生すると、次の例外が発生します。

OLE呼び出しを行う前に、現在のスレッドをシングルスレッドアパートメント(STA)モードに設定する必要があります。メイン関数にSTAThreadAttributeがマークされていることを確認してください。

バックグラウンドワーカースレッドをシングルスレッドアパートメントとしてマークするにはどうすればよいですか?私のProgram.csのMain呼び出しには既にその属性が含まれています。

26
KallDrexx

これは不可能です。BGWはスレッドプールスレッドを使用します。 TPスレッドは常にMTAであり、変更できません。通常のスレッドを使用して、開始する前にSetApartmentState()を呼び出す必要があります。このスレッドは、Application.Run()を呼び出してメッセージループもポンプする必要があります。

たぶん、このコードをUIスレッドから呼び出すことを検討する必要があります。とにかく、COMサーバーはとにかくUIスレッドでそのメソッドを実行しているためです。ワーカースレッドからCOMサーバーを作成したSTAスレッドへのマーシャリング呼び出しは自動的に行われ、COMがそれを処理します。

または、角の近くにいる雄牛を連れて、自分を整理します。独自のSTAスレッドを作成して、サーバーに幸せな家を与えることができます。コードは this post にあります。必ず、Initialize()オーバーライドでCOMオブジェクトを作成してください。

36
Hans Passant

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

注:これは、アプリケーションの残りの部分にパフォーマンスの影響を与える可能性があるため、(たとえばstateまたはdパラメータ)。

8
Simon Mourier

私はテストしていませんが、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
    });
}
4
Conrad de Wet

通常は、エントリポイントで属性[STAThread()]を定義して設定します(例:静的メイン)。

1
Aliostad

+ Conrad de Wetのアイデアを使用しましたが、うまくいきました!

ただし、このコードには小さな問題が1つあります。});のように、「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
        });
    }
0
moont10