現実には、Invoke とその仲間では、破棄されたコンポーネントでの呼び出し、またはハンドルの欠落による InvalidOperationException の取得を完全に保護することはできません。プリエンプティブ テストまたはロック セマンティクスを使用しても完全に解決できない、実際の根本的な問題に対処するスレッドのいずれかで、以下のような答えをまだ実際に見ていません。
通常の「正しい」イディオムは次のとおりです。
// the event handler. in this case preped for cross thread calls
void OnEventMyUpdate(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return; // ignore events if we arn't ready, and for
// invoke if cant listen to msg queue anyway
if (InvokeRequired)
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
else
this.MyUpdate(e.MyData);
}
// the update function
void MyUpdate(Object myData)
{
...
}
根本的な問題:
Invoke 機能を使用する場合、Windows メッセージ キューが使用されます。これは、Post または Send メッセージとまったく同じように、メッセージをキューに配置して、クロス スレッド呼び出しを待機するか、ファイア アンド フォーゲットします。Invoke メッセージの前に、コンポーネントとそのウィンドウ ハンドルを無効にするメッセージがある場合、または実行しようとしたチェックの直後にメッセージが配置された場合、問題が発生します。
x thread -> PostMessage(WM_CLOSE); // put 'WM_CLOSE' in queue
y thread -> this.IsHandleCreated // yes we have a valid handle
y thread -> this.Invoke(); // put 'Invoke' in queue
ui thread -> this.Destroy(); // Close processed, handle gone
y thread -> throw Invalid....() // 'Send' comes back, thrown on calling thread y
コントロールが自分自身をキューから削除しようとしていることを知る実際の方法はなく、呼び出しを「元に戻す」ためにできることは本当に合理的ではありません。何度チェックしたり、追加のロックを作成したりしても、他の誰かがクローズや非アクティブ化のようなものを発行するのを止めることはできません。これが発生するシナリオはたくさんあります。
解決策:
最初に認識すべきことは、(IsHandleCreated) チェックがイベントを無視した場合と同じように、呼び出しが失敗するということです。目標が非 UI スレッドで呼び出し元を保護することである場合は、例外を処理し、成功しなかった他の呼び出しと同様に処理する必要があります (アプリがクラッシュしないようにするため、またはその他のことを行うため)。リロール 機能を呼び出します。キャッチは、知る唯一の方法です。
// the event handler. in this case preped for cross thread calls
void OnEventMyWhatever(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return;
if (InvokeRequired)
{
try
{
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
}
catch (InvalidOperationException ex) // pump died before we were processed
{
if (this.IsHandleCreated) throw; // not the droids we are looking for
}
}
else
{
this.MyUpdate(e.MyData);
}
}
// the update function
void MyUpdate(Object myData)
{
...
}
例外フィルタリングは、ニーズに合わせて調整できます。ほとんどのアプリケーションでは、多くの場合、ワーカー スレッドには、UI スレッドが行う便利な外部例外処理とログ記録がすべて備わっているわけではないことに注意してください。または、それらすべてをログに記録して再スローします。多くの場合、ワーカー スレッドでキャッチされない例外は、アプリがクラッシュすることを意味します。