トップ回答者
BackgroundWorker から値を受ける方法

質問
-
お世話になります。
BackgroundWorker の理解が不十分なので、質問させていただきます。
たとえば、重いファイルを開く処理を BackgroundWorker で行い、
その処理が成功したかどうかや、エラーメッセージを渡すなど、
以下のようなコードの中で行いたいのです。
Code Snippetprivate 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 の引数として受けるのでしょうか?
どうぞ、よろしくお願いします。
回答
-
コピーとコピー先ファイルを開く処理を並行で動作させるのは危険すぎるのでやめたほうがよいかと。
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イベントハンドラで成否判定ができるようになると思います。
すべての返信
-
yem さんこんにちは
BackgroundWorkerで非同期に実行した場合は、UIのコントロールに結果を反映する処理がある場合、RunWorkCompletedイベントの引数RunWorkerCompletedEventArgs.Resultで結果を取得して、処理をしたほうがよいと思います。
RunWorkerCompletedイベントはUIスレッド上で実行されます。
ちなみに、RunWorkderCompltedEventArgs.Errorプロパティで非同期処理中に処理されなかった例外を取得できます。
なので、bgWorker_DoWorkメソッド内で、DoWorkEventArgs.Resultに処理結果を設定して、RunWorkerCompletedイベントハンドラで結果を受け取って、処理をするとよいともいます。以下例。
Code Snippetvoid bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null) // 非同期処理中にキャッチされなかった例外がある場合
{
MessageBox.Show(e.Error.Message);
return;
}
if ((bool)e.Result == true)
{
MessageBox.Show("成功");
return;
}
} -
ご回答ありがとうございます。
下記の方法で試してみましたが、うまくいきません。
なお、ファイルをコピーして、それを開くのが成功したら、フォームを閉じようとしています。
Code Snippetprivate 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();
}
}
ファイルのコピー、開く処理の成功、失敗に関わらず、常に、フォームが閉じてしまいます。
全然おかしなコードになっているせいとは思いますが、直す方法が分かりません。
お願いします。
-
try,catchで例外がキャッチされると、RunWorkerCompletedイベントハンドラでe.ErrorがNULLになります。ので、以下のようにすればよいかと。
Code Snippetprivate void OpenInspectsExcelFile(BackgroundWorker bw)
{
File.Copy("@C:\SAVE\Test.xls",
"@C:\TEST\Test.xls", true);
Process.Start(@"C:\TEST\Test.xls");
}ちなみに、BackgroundWorkerは引数で指定しなくても、クラス内ならどこでもインスタンス変数bgWorkerで参照できると思います。
-
どうもおかしいのです。
ちなみに、コードを分解し、BackgroundWorker を2つにしてみました。
copyWorker と、opemWorker の2つです。
コピーと開く処理に成功したら、終了してフォームを閉じようとしています。
また、このままではe.Error は、常にnull ですし、当然とはいえエラーメッセージが渡されません。
エラーメッセージを渡す方法と、処理が成功したかどうかを取得したいのですが...。
Code Snippetprivate 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();
}
-
コピーとコピー先ファイルを開く処理を並行で動作させるのは危険すぎるのでやめたほうがよいかと。
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イベントハンドラで成否判定ができるようになると思います。
-
恐れ入ります。
依然として、DoWorkメソッド内で例外をキャッチして、それを受ける方法がわからないのです。
また、例外発生時のエラーメッセージを受けたいのですが、それも分からぬまま試行錯誤中です。
で、RunWorkerCompleted でのe.Error も常にnullを返してきます。
BackgroundWorker では、デバッグで値を確かめられないのがネックです。
<追伸>
開くファイルを閉じて処理すると、成功します。
開くファイルを開いたままでデバッグしていますので、そのせいでCopy ができないためのエラーが発生しているはずです。
が、コピーする処理でエラーが発生したことをも受けるにはどうすべ気なのでしょうか?
-
yem さんからの引用 依然として、DoWorkメソッド内で例外をキャッチして、それを受ける方法がわからないのです。
うーん、すでに書かれていることなのですが、DoWork の中に try を書かないでおけば、例外が発生したときに Completed の e.Error に発生した例外が格納されます。この部分を見落とされていませんか?例外に応じてカスタムな例外を生成したいならば、自身で例外を throw する必要があります。カスタムな例外は生成する必要がないが、例外に対応する処理を記載したい場合には、Code Snippetprivate void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e){try{// 例外が発生するかもしれない処理}catch{// 例外が発生した場合の処理// 例外を外に投げる (この部分で e.Error に例外が設定される)throw;}}みたいなかんじでいいかな。