BackGroundWorkerを使った時の初回例外

タグの編集
投稿者 ビアンコ  (社会人) 投稿日時 2022/8/10 18:32:22
VS2013のVBで作られたWindows Formアプリを修正しています。
今回、とあるFormでBackGroundWorkerを使っているのですが、
ProgressChangedイベントで対象Form上のLabelのTextを書き換えようとすると、
デバッグ時に以下の初回例外メッセージが表示されます。

型 'System.InvalidOperationException' の初回例外が System.Windows.Forms.dll で発生しました
追加情報:有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'Label1' がアクセスされました。

具体的には、Label1.TextにProgressChangedのイベント引数に渡したStringを入れようとしています。

ProgressChangedイベントはForm(UI)スレッドで動作していると思っていて、
これで問題ないのだと思っているのですが、原因が思い当たりません。

試しに、ProgressChangedイベントをコメントアウトすると、RunWorkerCompletedイベントで
Formコントロール変数を参照しようとすると同様の初回例外メッセージが表示されます。
そもそも初回例外って何でしょうか?

また、デバッグではなく、ビルドしたexeを実行すると、上記問題は一切発生しません。
投稿者 るきお  (社会人) 投稿日時 2022/8/11 09:19:20
Visual Studio 2022 + .NET Framework 4.8 で試してみましたが、現象は再現せず正常に動作しました。

次のコードで試しました。
フォームにはButton1, Label1, BackgroundWorkder1 を配置しています。


Imports System.ComponentModel

Public Class Form1

    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
        BackgroundWorker1.WorkerReportsProgress = True
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)

        While True
            Task.Delay(1000).Wait()
            worker.ReportProgress(Now.Second)
        End While

    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Label1.Text = (e.ProgressPercentage.ToString)
    End Sub

End Class


Visual Studio 2013では試していません。このコードでも現象が発生しますか?

プログラムが動かなくて困っているときは、現象が再現できるコンパクトなプログラムを作って載せていただくと手間が省けます。

一点気になったのはここです。
> 具体的には、Label1.TextにProgressChangedのイベント引数に渡したStringを入れようとしています。
ReportProgress メソッドの引数の話をされていますでしょうか?このメソッドの引数には文字列を渡せません。
ProgressChangedイベントの第2引数 ProgressChangedEventArgs の ProgressPercentage も Integerなので直接イベントハンドラーを呼び出しているとしても文字列は渡せませんし、どういうプログラムをしているのかなと思いました。
こういった点を簡潔に伝えるためにもやはり現象が再現するコンパクトなプログラムがあるとよいですね。
投稿者 (削除されました)  () 投稿日時 2022/8/11 09:21:47
(削除されました)
投稿者 (削除されました)  () 投稿日時 2022/8/12 00:41:08
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/8/12 01:15:21
【るきお さん】
> ReportProgress メソッドの引数の話をされていますでしょうか?
> このメソッドの引数には文字列を渡せません。
ReportProgress(Integer, Object) メソッドなオーバーロードで渡せますよ。
Integer の方は、ReportProgress の e.ProgressPercentage への受け渡しで、
Object の方は、ReportProgress の e.UserState への受け渡しですね。


それ以外でワーカースレッドと UI スレッドを繋ぐものとしては、下記の機能があります。

UI スレッドからワーカースレッドへ受け渡せる情報:
 RunWorkerAsync(Object) メソッド → DoWork での e.Argument
 CancelAsync() メソッド → CancellationPending プロパティ

ワーカスレッドから UI スレッドで受け渡せる情報:
 DoWork での e.Cancel → RunWorkerCompleted での e.Cancelled
 DoWork 内での例外 → RunWorkerCompleted での e.Error
 DoWork 内での e.Result → RunWorkerCompleted での e.Result

※RunWorkerCompleted の e.UserState は使われません。


【ビアンコ さん】
>> 今回、とあるFormでBackGroundWorkerを使っているのですが、
>> ProgressChangedイベントで対象Form上のLabelのTextを書き換えようとすると、
× BackGroundWorker
○ BackgroundWorker

DoWork 内で、フォームのフィールド変数やコントロールにはアクセスしていない、ということですよね。
(ワーカースレッドからの Invoke や BeginInvoke の呼び出しも行っていないという想定です)

RunWorkerAsync() を呼んだ場合と、RunWorkerAsync(Object) を呼んだ場合とでは
内部処理の System.ComponentModel.AsyncOperationManager の扱いが多少異なるものの、
ReportProgress を DoWork 内から呼ぼうと、UI スレッドから呼ぼうと、
ProgressChanged は UI スレッドに対して通知されると認識していたのですが…。


>> デバッグ時に以下の初回例外メッセージが表示されます。
Debug ビルド時のみの事象で、Release ビルド時には再現しない状況でしょうか。

それと、そのメッセージがどこに表示されるのかも確認させてください。
イミディエイトウィンドウ(あるいは出力ウィンドウ)に表示されるだけで、
動作そのものは問題は出ていない状況でしょうか。
あるいは例外として処理が中断してしまっている状況でしょうか。

後者の例外中断の場合は、どの行で停止しているのかを確認して、
その時の StackTrace を追跡してみてください。

前者の出力メッセージだけのものだとすると、[デバッグ]-[ウィンドウ]-[例外設定]で
キャプチャする設定になっているのかもしれません。
(2013 の初期設定がどうなっているかは分かりませんが)


>> VS2013のVBで作られたWindows Formアプリを修正しています。
Visual Studio 2013 で作成されたプロジェクトを、
Visual Studio 2013 で修正している、という状況でよろしいでしょうか。

そのプロジェクトの .NET Framework バージョンを教えてください。
あと、ビルドターゲット(x86, AnyCPU 等)も知りたいです。

2013 は手元に環境が無いので試せていないのですが、少なくとも
VS2022 Enterprise + .NET Framework 4.8.1 + Windows 11 21H2 環境で試したところ、
当方では事象を再現するには至っていません。

また、それぞれのイベントで Thread.CurrentThread.ManagedThreadId を確認してもみましたが、
ProgressChanged  が非 UI スレッドとなる現象は今のところ確認できていません。
投稿者 ビアンコ  (社会人) 投稿日時 2022/8/12 16:11:31
るきお様、 魔界の仮面弁士様、ありがとうございます。
こちらの状況をお伝えしたいのですが、ちょっと急用が出来てしまいまして、
誠に申し訳ございませんが、お時間をください。
なお、Label表示用にReportProgressのイベント引数に渡しているStringの件は、
魔界の仮面弁士様のご説明の方法で行っています。
もしかしたら、私の環境の問題かも知れませんが、
今までこんな事はなかったです。