自動テスト実行アプリケーションを作成しています。アプリケーションのこの部分では、ポーリングサーバーで作業しています。これは、Webサーバーを常にポーリングして、新しい自動テストをいつ実行するかを決定することによって機能します(GUIアプリケーションの夜間自動実行の場合)。
ポーリングサーバーが要求を確認すると、必要なすべての情報をダウンロードし、バックグラウンドワーカーでテスト実行を実行します。問題は、テスト実行の一部にOLE、COM、およびバックグラウンドワーカースレッドで発生するその他の呼び出し(たとえば、Clipboard.Clear()
)があることです。これらの呼び出しの1つが発生すると、次の例外が発生します。
OLE呼び出しを行う前に、現在のスレッドをシングルスレッドアパートメント(STA)モードに設定する必要があります。メイン関数にSTAThreadAttributeがマークされていることを確認してください。
バックグラウンドワーカースレッドをシングルスレッドアパートメントとしてマークするにはどうすればよいですか?私のProgram.csのMain呼び出しには既にその属性が含まれています。
これは不可能です。BGWはスレッドプールスレッドを使用します。 TPスレッドは常にMTAであり、変更できません。通常のスレッドを使用して、開始する前にSetApartmentState()を呼び出す必要があります。このスレッドは、Application.Run()を呼び出してメッセージループもポンプする必要があります。
たぶん、このコードをUIスレッドから呼び出すことを検討する必要があります。とにかく、COMサーバーはとにかくUIスレッドでそのメソッドを実行しているためです。ワーカースレッドからCOMサーバーを作成したSTAスレッドへのマーシャリング呼び出しは自動的に行われ、COMがそれを処理します。
または、角の近くにいる雄牛を連れて、自分を整理します。独自のSTAスレッドを作成して、サーバーに幸せな家を与えることができます。コードは this post にあります。必ず、Initialize()オーバーライドでCOMオブジェクトを作成してください。
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パラメータ)。
私はテストしていませんが、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
});
}
通常は、エントリポイントで属性[STAThread()]
を定義して設定します(例:静的メイン)。
+ 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
});
}