Jul 02

The strange case of the BackgroundWorker and the disappearing exception

I was recently building a simple GUI in .NET to operate an algorithm as part of a school project, and I encountered a weird problem using BackgroundWorkers. I spent a lot of time debugging it, mainly because the code seemed to be perfect (which was true) but the run-time behavior was so strange...

Anyway, to make my algorithm as weakly-coupled as possible decided not to use 'BackgroundWorker.ReportProgress', because then my algorithm will have to know what a BackgroundWorker is...
I decided to actually fire my own event whenever I wanted to report on the algorithm progress (which is rather lenghty). So I defined my delegate and event inside my one-function class that runs the algorithm:

delegate System::Void AlgProgressDelegate(unsigned int done, unsigned outOf);

ref class Algorithm {
event AlgProgressDelegate^ AlgProgress;

public: void StartAlgorithm() {
AlgProgress(done, outOf);

Meanwhile, back in my UI...

public: System::Void OnProgress(unsigned int done,unsigned int outOf) {
progressBar1->Maximum = outOf;
progressBar1->Value = done;
if((done % 100) == 0) {
label4->Text = String::Format("{0}/{1}",done,outOf);

Of course I registered my form to recieve these events:

Algorithm a();
a.AlgProgress += gcnew AlgProgressDelegate(this, &Form1::OnProgress);

Naively, I thought this would run perfectly, as I could see nothing wrong in the code, can you?
Little did i know that .NET has a few surprises when it comes to updating UI controls from the event thread...

I ran my little program, expecting the nice progress bar to fill up as the algorithm progresses, but instead - the algorithm would just end abruptly, very close to the beginning, without any exceptions or anything.

So I started to debug the algorithm function to see whats wrong: Step over, Step over, Step over and WHAM! the instruction pointer just leaps 20 lines ahead totally disregarding for loops and ifs in the middle.. What the hell is going on?

The mysterious jump ahead occured over the "AlgProgress()" function call. Some line-remarking within that function revealed that the offending line is the "progressBar1->Maximum" property set. Strangely enough, the "progressBar1->Value" prop set was executed just fine... This is weird stuff.

I don't remember exactly how, but finally I came up with a thought that maybe an exception is thrown and no one is catching it.. So I wapped the inside of my "AlgProgress" function with a try-catch block.
And lo and behold, indeed an exception is thrown. A nice "System.InvalidOperationException:
Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on."
But the thing is, I was kinda getting used to idea that when exceptions are thrown - something catches them, and if nothing catches them the run-time is somehow stopped... so us developers/users will be anounced of said exception and be able to deal with it/bash the screen into a pulp.
But I guess Microsoft couldn't go without a good Voodoo in their framework, so they put in the "Disappearing exception" concept.

From here on it was quite simple to find a solution. I used the "CheckForIllegalCrossThreadCalls" property of the progress bar, although the better solution would be to have the UI thread change the progress bar by using "Invoke(delegate..)", as explained here.

To summerize: When in doubt - try & catch.