何度も調べ直し&テストしてるので、自分用にメモ。
メインフォームからサブフォームを作りShow()し、メインフォームの操作に応じてサブフォームの非同期メソッドを呼び出しサブフォームに色々させたりその結果を受け取ったりしたい。例えばサブフォームにブラウザコントロールが表示されててそれ操作し結果を返させたり。
ただその間に入ってるメソッドが基底クラスのvirtualをoverrideしたものだったりして、気軽にasync Taskに出来ない場合、困ったことになる。(メインフォームが一定時間固まるのは気にしないとしても)
同期メソッドから非同期メソッドを呼び出し、その完了を待たねばならぬのだが、
1.asyncに出来ないのでawaitは使えない。
2.Wait()を使うとメインフォームだけじゃなくサブフォームのメッセージループも止まる(両フォーム同じスレッドだから)ので、例えばWebView2.NavigationCompleted等を待つことも出来ずソフト全体が固まる。
3.Task.Run()で別スレッドにしたら、スレッドをまたぐことになりサブフォームのコントロールを操作できない。
袋小路かと思いきや…解決策はある。例えば以下2通り。
Task t = subform.HogeAsync();
//呼び出すと、呼び出し先のawaitまで実行した後戻ってきて下の行へ進む。awaitから先はメッセージループでメッセージ拾った後実行される。
t.ContinueWith((result) => { /*
非同期メソッド終了後の処理*/ }, TaskContinuationOptions.ExecuteSynchronously);
//その間に非同期メソッド終了後の処理
を登録しておく。
return;
//このメソッドを抜け、メッセージループに戻り、メッセージを拾うと、呼び出し先のawait以降の処理が実行され、その後非同期メソッド終了後の処理が実行される。
ただし、awaitで戻る前に例外で戻った場合、ContinueWith()で非同期メソッド終了後の処理が即座に実行され、その後returnする。
また、両タスク内の例外は外側でtry~catchで捕まえられないので注意。HogeAsync()内の例外はContinueWith()で登録された処理内で見れるが、その処理内の例外をどうするかが問題。まあ数珠繋ぎでいいんだろうけど。
あとContinueWith()の第2引数はTaskScheduler.FromCurrentSynchronizationContext()にしとかないと別スレッドになっちゃうかも。その辺の理解まだ甘いなー。
Task t = subform.HogeAsync();
while (!t
.IsCompleted)
{
Thread.Sleep(100);
Application.DoEvents();
}
細かい所は省略。再入禁止処理等々。
他、Invokeとか使う方法もあるかな?
あとはここにメモしたことを忘れない様にせねば。まあ忘れても調べ直しで検索した時ここがヒットするから大丈夫か。