C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト


■96002 / )  Task起動先が、Task起動元の終了を検知する方法について
□投稿者/ taro (11回)-(2020/10/15(Thu) 10:33:04)

分類:[C#] 

VisualStudio2019で、C#の学習をしております。
当方、C++やJAVA等の他言語は多少の経験がありますが、C#は未経験です。

以下のようなテストアプリを作って、Taskの学習をしています。

・アプリ起動すると、ボタンが1つだけのForm1が起動する。
・Form1のボタンを押すと、Form2が起動する(Form1はForm2を起動する為だけのもの)。
・Form2は
 ・終了ボタン(押すとForm2を終了し、処理をForm1に戻す)「button1」
 ・ログ出力テキストボックス「textBox1」
 だけがある。
・Form2起動時(Shownイベント)に、「処理タスク」を起動する。
・「処理タスク」は、ログ出力テキストボックスに対して、1秒間隔で"処理"という文字列を出力する。


■困っていること

Form2を起動し、1秒間隔で"処理"と出力されている途中に、終了ボタンを押すと
-----
System.InvalidOperationException: 'ウィンドウ ハンドルが作成される前、コントロールで Invoke または BeginInvoke を呼び出せません。'
-----
が発生する。
原因は、ログ出力テキストボックスがあるForm2が既に閉じられている為と思われる。


■Form2のコード(Form1は割愛)

public partial class Form2 : Form
{
  public Form2()
  {
    InitializeComponent();
  }

  /// <summary>
  /// 「終了」ボタン押下
  /// </summary>
  private void button1_Click(object sender, EventArgs e)
  {
    // フォームを閉じて上位に返す
    this.DialogResult = DialogResult.OK;
    this.Close();
  }
  

  /// <summary>
  /// フォーム表示直後に一度だけ実行されるShownイベントのコールバック
  /// </summary>
  private void Form2_Shown(object sender, EventArgs e)
  {
    // テキストボックスにログ出力
    // (このテキストボックスは、メインタスクからも処理タスクからも出力処理が行われる)
    textBox1.AppendText("UDP通信タスクを起動します" + "\r\n");

    // 「処理タスク」を起動
    Task task = Task.Run(() => {
      RunProcTask();
    });
  }

  /// <summary>
  /// 処理タスク
  /// </summary>
  private void RunProcTask()
  {
    // 1秒ごとにログを出力する
    while (true) {
      // 1秒待ってからログ出力
      Thread.Sleep(1000);
      //textBox1.AppendText("処理" + "\r\n");  //←このような出力はできない
      Invoke(new Action<string>(this._PutLog), "処理");  //←ここで例外が発生する
    }
  }

  /// <summary>
  /// 処理タスクからログ出力を行う
  /// </summary>
  private void _PutLog(string msg)
  {
    textBox1.AppendText(msg + "\r\n");
  }
}


■教えてほしいこと

・処理タスクからログを出力する方法として、色々調べた結果上記のようにInvokeを用いていますが、
 効率や可読性・処理速度等、総合的に見てベストな対応でしょうか?
・InvalidOperationExceptionを回避する方法として
 ・Form2に「static bool flg;」というメンバを用意し、Form2_Shownメソッド冒頭でtrueセット、button1_Clickメソッド冒頭でfalseセット。
 ・「Invoke(new Action<string>(this._PutLog), "処理");」は、flgがtrueの場合のみ実行
 という対応をしました。
 「Form1起動→ボタンを押下しForm2起動→"処理"出力中に終了ボタン押下→無事Form1に戻る」まではいいのですが、
 再度Form1のボタンを押下しForm2を起動したところ、
 System.InvalidOperationException: 'ウィンドウ ハンドルが作成される前、コントロールで Invoke または BeginInvoke を呼び出せません。'
 というエラーが発生しました。
 何か足らないことがあるのでしょうか?
・上記の「Form2が存在するかどうかをフラグで判定」という方法はベストではない気がしています。
 本当は、終了ボタン押下時に「処理タスクを止める」という事をしたかったのですが、やり方が分からず・・・。
 ベストな実装はどのようなものでしょうか?


長くなりましたが、よろしくお願い致します。


 



返信 編集キー/


管理者用

- Child Tree -