I have a fairly complex program so I won't dump the whole thing in here. Here's a simplified version:
class Report {
private BackgroundWorker worker;
public Report(BackgroundWorker bgWorker, /* other variables, etc */) {
// other initializations, etc
worker = bgWorker;
}
private void SomeCalculations() {
// In this function, I'm doing things which may cause fatal errors.
// Example: I'm connecting to a database. If the connection fails,
// I need to quit and have my background worker report the error
}
}
// In the GUI WinForm app:
// using statements, etc.
using Report;
namespace ReportingService {
public partial class ReportingService : Form {
// My background worker
BackgroundWorker theWorker = new BackgroundWorker() {
WorkerReportsProgress = true
};
// The progress changed event
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) {
// e.UserState and e.ProgressPercentage on some labels, etc.
}
// The do work event for the worker, runs the number crunching algorithms in SomeCalculations();
void worker_DoWork(object sender, DoWorkEventArgs e) {
Report aReport = e.Argument as Report;
aReport.SomeCalculations();
}
// The completed event, where all my trouble is. I don't know how to retrieve the error,
// or where it originates from.
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
// How, exactly, do I get this error message? Who provides it? How?
if (e.Error != null) {
MessageBox.Show("Error: " + (e.Error as Exception).ToString());
}
else if (e.Cancelled) {
MessageBox.Show("Canceled");
}
// operation succeeded
else {
MessageBox.Show("Success");
}
}
// Initialization of the forml, etc
public ReportingService() {
InitializeComponent();
theWorker.ProgressChanged += worker_ProgressChanged;
theWorker.DoWork += worker_DoWork;
theWorker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
// A button that the user clicks to execute the number crunching algorithm
private void sumButton_Click(object sender, EventArgs e) {
Report myReport = new Report(theWorker, /* some other variables, etc */)
theWorker.RunWorkerAsync(myReport);
}
}
}
Here's my logic, and please correct me if I'm going about this the wrong way:
I abstracted the class out of the GUI because it's ~2000 lines and needs to be it's own self contained object.
I pass the background worker into my class so that I can report back the progress of my number crunching.
What I don't know how to do is let the background worker know that an error has happened inside my class. In order to get the RunWorkerCompleted argument as an exception, where does my try/catch block need to go, and what should I do in the catch block?
Thanks for your help!
EDIT:
I've tried the following things to test the error handling:
Keep in mind I corrupted my database connection string to purposefully receive an error message.
In my class I do:
// My number crunching algorithm contained within my class calls a function which does this:
// try {
using (SqlConnection c = GetConnection()) { // note: I've corrupted the connection string on purpose
c.Open(); // I get the exception thrown here
using (SqlCommand queryCommand = new SqlCommand(query, c)) { /* Loop over query, etc. */ }
c.Close();
}
// } catch (Exception e) { }
1.
From my understanding, an unhandled exception gets cast to the Error
portion of the RunWorkerCompletedEventArgs
? When I try this I get the following:
// In my winform application I initialize my background worker with these events:
void gapBW_DoWork(object sender, DoWorkEventArgs e) {
Report aReport = e.Argument as Report;
Report.Initialize(); // takes ~1 minute, throws SQL exception
Report.GenerateData(); // takes around ~2 minutes, throws file IO exceptions
}
void gapBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) { // I can't get this to trigger, How does this error get set?
MessageBox.Show("Error: " + (e.Error as Exception).ToString());
}
else if (e.Cancelled) {
MessageBox.Show("Canceled: " + (e.Result).ToString());
}
else {
MessageBox.Show("Success");
}
}
Visual studio says that my application chokes on c.Open()
failing with an unhandled exception.
2. When I put a try/catch block in my DoWork function:
void gapBW_DoWork(object sender, DoWorkEventArgs e) {
try {
Report aReport = e.Argument as Report;
aReport.Initialize(); // throws SQL exceptions
aReport.GenerateData(); // throws IO file exceptions
}
catch (Exception except) {
e.Cancel = true;
e.Result = except.Message.ToString();
}
}
void gapBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) { // I can't get this to trigger, How does this error get set?
MessageBox.Show("Error: " + (e.Error as Exception).ToString());
}
else if (e.Cancelled) {
MessageBox.Show("Canceled: " + (e.Result).ToString());
}
else {
MessageBox.Show("Success");
}
}
I get a TargetInvocationException
was unhandled in Program.cs at the automatically generated Application.Run(new ReportingService());
line. I placed a breakpoint on my RunWorkerCompleted and can see that e.Cancelled = true, e.Error = null, and e.UserState = null. The message contained within e.Cancelled is simply "Operation has been cancelled". I imagine I'm receiving the TargetInvocationException
from an invalid cast of e.Result (since it's null). What I want to know though, is how come e.Error is still null and e.Canceled doesn't contain any helpful information about why the operation was canceled?
3.
When I tried setting e.Canceled = true;
from within DoWork on an exception catch, I managed to trigger the else if (e.Cancelled) {
line in my RunWorkerCompleted
function. I thought this was reserved for the user requesting the job being canceled though? Am I fundamentally misunderstanding how the background worker functions?