DOBON.NETプログラミング道掲示板

■34817 / 親記事)  DLL内のスレッドからイベントを発行すると、受信したフォームでラベルが更新できない
  
□投稿者/ tomy 一般人(8回)-(2021/07/02(Fri) 14:54:59)
  • アイコン環境/言語:[Windows10/C#/VisualStudio2019/.NET Framework4.8] 
    分類:[.NET] 

    VisualStudio 2019のC#でDLL内でスレッドを作成し、スレッドからイベントを発行するようにしています。

    具体的には、フォームからDLLのポーリング開始メソッドを呼び出す。
    ポーリング開始メソッドはスレッドを作成して処理を終了(フォームに制御が戻る)

    スレッドは外部機器に対して定期的に通信のポーリングを実行し、情報を読みだして内部の変数に保存します。
    状態変化したら、状態変化イベントを発行します。

    状態変化イベントはフォーム側で受け取って、ラベルにデータを表示する。

    というようなものを作成していますが、下記のエラーが発生します。

    ---------------
    例外がスローされました: 'System.InvalidOperationException' (System.Windows.Forms.dll の中)
    有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'label3' がアクセスされました。
    ---------------


    異なるスレッドからラベルにアクセスした為にエラーになっています。

    フォーム側でラベルに表示するメソッドを書いて、

    private void DispValue(decimal val)
    {
    label3.Text = val.ToString();
    }

    フォームのイベント処理で下記のようにInvokeで呼び出せばラベルを更新はできました。

    this.Invoke(new Action<decimal>(this.DispValue), CommDll.Instance.Value);

    しかし、スレッド自体はDLL内部にあるため、フォーム側でそれを意識するのは話が違うなあと思っております。

    DLL内のスレッドからフォームと同じスレッドの処理(?)に情報を渡して、そこからイベント発行すればラベルの更新もできるのかな?と思うのですが、方法がわからず・・・。
    何か良い手段はないでしょうか。

マルチポストを報告
違反を報告
引用返信 削除キー/
■34818 / ResNo.1)  Re[1]: DLL内のスレッドからイベントを発行すると、受信したフォームでラベルが更新できない
□投稿者/ 魔界の仮面弁士 大御所(1367回)-(2021/07/02(Fri) 15:59:03)
  • アイコン2021/07/02(Fri) 16:05:34 編集(投稿者)

    No34817に返信(tomyさんの記事)
    > 異なるスレッドからラベルにアクセスした為にエラーになっています。
    御存知の通り、「Label」や「Form」を操作できるのは、
    その UI スレッドのみです。

    同期的な呼び出しであれ非同期的な呼び出しであれ、
    他のスレッドから直接操作してはいけません。

    > しかし、スレッド自体はDLL内部にあるため、フォーム側でそれを意識するのは話が違うなあと思っております。

    別スレッドで発生したイベントなのであれば、意識しないと駄目ですよ。

    たとえばフォームに BackgroundWorker を貼って使った場合、
    DoWork イベントは UI スレッドではなく、ワーカースレッドとなりますよね。
    それと同じ話だと思います。

    private void button1_Click(object sender, EventArgs e)
    {
     Debug.WriteLine($"Click - {this.InvokeRequired} - {Thread.CurrentThread.ManagedThreadId}");
     this.backgroundWorker1.WorkerReportsProgress = true;
     this.backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
     Debug.WriteLine($"ProgressChanged - {this.InvokeRequired} - {Thread.CurrentThread.ManagedThreadId}");
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
     ((BackgroundWorker)sender).ReportProgress(64);
     Debug.WriteLine($"DoWork - {this.InvokeRequired} - {Thread.CurrentThread.ManagedThreadId}");
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
     Debug.WriteLine($"RunWorkerCompleted - {this.InvokeRequired} - {Thread.CurrentThread.ManagedThreadId}");
    }


    イベントが UI スレッドであるかどうかが分からない場合には、
    InvokeRequired プロパティで判断できます。
    UI スレッドに作業を依頼するメソッドは、御存知 Invoke/BeginInvoke です。


    > DLL内のスレッドからフォームと同じスレッドの処理(?)に情報を渡して、

    https://stackoverflow.com/questions/1698889/
違反を報告
引用返信 削除キー/
■34819 / ResNo.2)  Re[2]: DLL内のスレッドからイベントを発行すると、受信したフォームでラベルが更新できない
□投稿者/ tomy 一般人(9回)-(2021/07/02(Fri) 16:13:46)
  • アイコンNo34818に返信(魔界の仮面弁士さんの記事)
    >>しかし、スレッド自体はDLL内部にあるため、フォーム側でそれを意識するのは話が違うなあと思っております。
    >
    > 別スレッドで発生したイベントなのであれば、意識しないと駄目ですよ。

    うーん、なるほど、それではやはりイベントを受け取ってから、フォーム側で別スレッドかどうかを判断してやる必要があるのですね。


    >> DLL内のスレッドからフォームと同じスレッドの処理(?)に情報を渡して、
    >EventHandler<T> などの「戻り値が不要なデリゲート」ならばこんな感じ。

    ありがとうございます。
    もうちょっと調べてみます。

違反を報告
引用返信 削除キー/
■34820 / ResNo.3)  Re[3]: DLL内のスレッドからイベントを発行すると、受信したフォームでラベルが更新できない
□投稿者/ tomy 一般人(10回)-(2021/07/02(Fri) 16:44:38)
  • アイコンありがとうございました。
    教えていただいた方法で無事にUIスレッドにイベントを発行できるようになりました。



解決み!
違反を報告
引用返信 削除キー/



スレッド内ページ移動 / << 0 >>

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -