none
BackgroundWorker から値を受ける方法 RRS feed

  • 質問

  •  

    お世話になります。

    BackgroundWorker の理解が不十分なので、質問させていただきます。

     

    たとえば、重いファイルを開く処理を BackgroundWorker で行い、

    その処理が成功したかどうかや、エラーメッセージを渡すなど、

    以下のようなコードの中で行いたいのです。

    Code Snippet

    private void openButton_Click(object sender, EventArgs e)

    {

    bgWorker.RunWorkerAsync();

     

    // 処理が成功したかのブール値や、渡された文字列をここで利用したい。

    }

    private void bgWorker_DoWork(object sender, DoWorkEventArgs e)

    {

    try

    {

    Process.Start(@"C:\TEST\Test.xls");

    }

    catch(Exception ex)

    {

    // ここの ex.Message の文字列を受けて処理したい。

    }

    }

     

    それとも、RunWorkerCompleted を使うのでしょうか?

    また、RunWorkerAsync の引数として受けるのでしょうか?

     

    どうぞ、よろしくお願いします。

    2008年6月1日 1:23

回答

  • コピーとコピー先ファイルを開く処理を並行で動作させるのは危険すぎるのでやめたほうがよいかと。

    yemさんと同じようにコピーしてファイルを開くプログラムを作ってみましたが動きます。Loadイベントでイベントハンドラを登録して、button1がクリックしたらコピーしてファイルを開く処理を行うようにしています。

     

    Code Snippet

            // ボタンクリックで非同期処理開始

         private void button1_Click(object sender, EventArgs e)
            {
                backgroundWorker1.RunWorkerAsync();
            }

     

            void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (e.Error != null)
                {
                    MessageBox.Show(e.Error.Message);
                    return;
                }
                else
                {
                    this.Close(); // フォームを閉じる
                }
            }

            void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                // コピー処理
                System.IO.File.Copy(@"D:\temp\test.xls", @"D:\temp\test2.xls");
                e.Result = System.Diagnostics.Process.Start(@"D:\temp\test2.xls"); // 正常系パターン
                //e.Result = System.Diagnostics.Process.Start(@"D:\temp\test23.xls"); // 例外発生パターン
            }

         // フォームロード時にイベントハンドラ登録

            private void Form1_Load(object sender, EventArgs e)
            {
                backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
                backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            }

     

     

    DoWorkメソッド内で例外をキャッチしたい場合は、DoWorkEventArgs.Resultに設定する値を工夫して成功、失敗を判定できるようにするか、もしくはキャッチした例外を再スローするににすればRunWorkerCompletedイベントハンドラで成否判定ができるようになると思います。

    2008年6月1日 10:19

すべての返信

  • yem さんこんにちは

     

    BackgroundWorkerで非同期に実行した場合は、UIのコントロールに結果を反映する処理がある場合、RunWorkCompletedイベントの引数RunWorkerCompletedEventArgs.Resultで結果を取得して、処理をしたほうがよいと思います。

    RunWorkerCompletedイベントはUIスレッド上で実行されます。

    ちなみに、RunWorkderCompltedEventArgs.Errorプロパティで非同期処理中に処理されなかった例外を取得できます。

    なので、bgWorker_DoWorkメソッド内で、DoWorkEventArgs.Resultに処理結果を設定して、RunWorkerCompletedイベントハンドラで結果を受け取って、処理をするとよいともいます。以下例。

     

    Code Snippet

           void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (e.Error != null) // 非同期処理中にキャッチされなかった例外がある場合
                {
                    MessageBox.Show(e.Error.Message);
                    return;
                }
                if ((bool)e.Result == true)
                {
                    MessageBox.Show("成功");
                    return;
                }
            }

     

     

    2008年6月1日 2:05
  • ご回答ありがとうございます。

     

    下記の方法で試してみましたが、うまくいきません。

    なお、ファイルをコピーして、それを開くのが成功したら、フォームを閉じようとしています。

    Code Snippet

    private void openButton_Click(object sender, EventArgs e)

    {

    bgWorker.RunWorkerAsync();

    }

    private void OpenInspectsExcelFile(BackgroundWorker bw)

    {

                try
                {
                    File.Copy("@C:\SAVE\Test.xls",
                        "@C:\TEST\Test.xls", true);
                    Process.Start(@"C:\TEST\Test.xls");
                }
                catch
                {
                    return;
                }
    }

    private void bgWorker_DoWork(object sender, DoWorkEventArgs e)

    {

    OpenInspectsExcelFile(bgWorker);

    }

    private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

    {

    string msg = "";

    if (e.Error != null)

    {

    msg = String.Format("An Error occurred: {0}", e.Error.Message);

    MessageBox.Show(msg, "エラー",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);

    }

    else

    {

    this.Close();

    }

    }

     

     

     

    ファイルのコピー、開く処理の成功、失敗に関わらず、常に、フォームが閉じてしまいます。

     

    全然おかしなコードになっているせいとは思いますが、直す方法が分かりません。

    お願いします。

    2008年6月1日 4:39
  • try,catchで例外がキャッチされると、RunWorkerCompletedイベントハンドラでe.ErrorがNULLになります。ので、以下のようにすればよいかと。

     

    Code Snippet

    private void OpenInspectsExcelFile(BackgroundWorker bw)

    {

                    File.Copy("@C:\SAVE\Test.xls",
                        "@C:\TEST\Test.xls", true);
                    Process.Start(@"C:\TEST\Test.xls");
    }

     

     

    ちなみに、BackgroundWorkerは引数で指定しなくても、クラス内ならどこでもインスタンス変数bgWorkerで参照できると思います。

    2008年6月1日 4:59
  • どうもおかしいのです。

     

    ちなみに、コードを分解し、BackgroundWorker を2つにしてみました。

    copyWorker と、opemWorker の2つです。

    コピーと開く処理に成功したら、終了してフォームを閉じようとしています。

     

    また、このままではe.Error は、常にnull ですし、当然とはいえエラーメッセージが渡されません。

    エラーメッセージを渡す方法と、処理が成功したかどうかを取得したいのですが...。

    Code Snippet

    private void openButton_Click(object sender, EventArgs e)

    {

    copyWorker.RunWorkerAsync();

    openWorker.RunWorkerAsync();

    }

    private void CopyExcelFile(BackgroundWorker bw)

    {

    string msg = "";

    try

    {

                    File.Copy("@C:\SAVE\Test.xls",
                        "@C:\TEST\Test.xls", true);

    }

    catch(Exception ex)

    {

    msg = ex.Message:

    }

    }

    private void OpenExcelFile(BackgroundWorker bw)

    {

    string msg = "";

    try

    {

                    Process.Start(@"C:\TEST\Test.xls");

    }

    catch(Exception ex)

    {

    msg = ex.Message:

    }

    }

    private void copyWorker_DoWork(object sender, DoWorkEventArgs e)

    {

    CopyExcelFile();

    }

    private void openWorker_DoWork(object sender, DoWorkEventArgs e)

    {

    OpemExcelFile();

    }

    private void copyWorker_RunWorkerCompleted(object sender,

    RunWorkerCompletedEventArgs e)

    {

    if (msg == "")

    msg = "コピーできません。";

    if (e.Error == null)

                    MessageBox.Show(msg, "エラー");

    }

    private void openWorker_RunWorkerCompleted(object sender,

    RunWorkerCompletedEventArgs e)

    {

    if (msg == "")

    msg="開けません。";

    if (e.Error == null)

                    MessageBox.Show(msg, "エラー");

    else

    this.Close();

    }

     

    2008年6月1日 9:49
  • コピーとコピー先ファイルを開く処理を並行で動作させるのは危険すぎるのでやめたほうがよいかと。

    yemさんと同じようにコピーしてファイルを開くプログラムを作ってみましたが動きます。Loadイベントでイベントハンドラを登録して、button1がクリックしたらコピーしてファイルを開く処理を行うようにしています。

     

    Code Snippet

            // ボタンクリックで非同期処理開始

         private void button1_Click(object sender, EventArgs e)
            {
                backgroundWorker1.RunWorkerAsync();
            }

     

            void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (e.Error != null)
                {
                    MessageBox.Show(e.Error.Message);
                    return;
                }
                else
                {
                    this.Close(); // フォームを閉じる
                }
            }

            void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                // コピー処理
                System.IO.File.Copy(@"D:\temp\test.xls", @"D:\temp\test2.xls");
                e.Result = System.Diagnostics.Process.Start(@"D:\temp\test2.xls"); // 正常系パターン
                //e.Result = System.Diagnostics.Process.Start(@"D:\temp\test23.xls"); // 例外発生パターン
            }

         // フォームロード時にイベントハンドラ登録

            private void Form1_Load(object sender, EventArgs e)
            {
                backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
                backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            }

     

     

    DoWorkメソッド内で例外をキャッチしたい場合は、DoWorkEventArgs.Resultに設定する値を工夫して成功、失敗を判定できるようにするか、もしくはキャッチした例外を再スローするににすればRunWorkerCompletedイベントハンドラで成否判定ができるようになると思います。

    2008年6月1日 10:19
  • 恐れ入ります。

    依然として、DoWorkメソッド内で例外をキャッチして、それを受ける方法がわからないのです。

    また、例外発生時のエラーメッセージを受けたいのですが、それも分からぬまま試行錯誤中です。

     

    で、RunWorkerCompleted でのe.Error も常にnullを返してきます。

    BackgroundWorker では、デバッグで値を確かめられないのがネックです。

     

    <追伸>

    開くファイルを閉じて処理すると、成功します。

    開くファイルを開いたままでデバッグしていますので、そのせいでCopy ができないためのエラーが発生しているはずです。

    が、コピーする処理でエラーが発生したことをも受けるにはどうすべ気なのでしょうか?

    2008年6月1日 13:09
  •  yem さんからの引用

    依然として、DoWorkメソッド内で例外をキャッチして、それを受ける方法がわからないのです。

     
    うーん、すでに書かれていることなのですが、DoWork の中に try を書かないでおけば、例外が発生したときに Completed の e.Error に発生した例外が格納されます。この部分を見落とされていませんか?
     
    例外に応じてカスタムな例外を生成したいならば、自身で例外を throw する必要があります。カスタムな例外は生成する必要がないが、例外に対応する処理を記載したい場合には、
     
    Code Snippet
     
    private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
      try
      {
        // 例外が発生するかもしれない処理
      }
      catch
      {
        // 例外が発生した場合の処理
     
        // 例外を外に投げる (この部分で e.Error に例外が設定される)
        throw;
      }
    }

     

     

    みたいなかんじでいいかな。
     
    2008年6月1日 16:38
  • handcraft さん、ありがとうございます。

    重要な点の日本語を取り違えていました。

     

    DoWork から try をなくしてしまえばエラーを受け取ることができるのですね。

    試したところ、その通りでした。

     

    時間がかかってしまいましたが、やっと処理を理解することができました。

    理解に時間がかかってしまったことを申し訳なく思います。

     

    また、K.Taoka さん、

    頭の中が、とんちんかんであるのを読み取られてしまいましたが、ご指摘の点を読み返しまして解決することができました。

    解説をありがとうございます。
    2008年6月3日 13:42