Monday, June 7, 2010

.NET and the Unending Process...

So I had a problem. In the scheme of things, it wasn't that big of a problem, at least provided that one didn't stop and restart an application multiple times without doing the same to their PC.

As one might surmise from the TITLE of this particular post, my problem was a process that just refused to die when my application closed.

This particular application is a rather massive WPF based Windows application, with hundreds (nearly a thousand) of source-controlled files, scores of icons, themes, and other references, thousands upon thousands of lines of code and to top it all off--the evil thing is multi-threaded.

So, to say that this is a complex application is a bit of an understatement, and of course to make it all THAT much better, active development on it stopped about this time last year, so the code is basically bone-dry in my brain.

Which of course meant that I had to find the cause of the problem, fix it and get it re-deployed within an hour. If not sooner.

Since I'm a good Consultant, I did not despair, but rather fired up my trusty copy of Visual Studio 2008 and started stepping through code.

My first thought was that this was something to do with THREADS! I mean, it just makes sense. After all, every time one turns around in this thing, a BackGroundWorker object is doing SOMETHING to data somewhere within its mass of code files or a timer is elapsing or... or something!

So, I go about, making sure that all my Timers and BackgroundWorkers and ThreadPools were successfully closing and getting disposed of when my application was shutting down.

Oddly, that made things worse.

Not to be out-done by mere code, I applied an ENVIRONMENT.EXIT(0) command in the application's Exit event handler.

"THAT should kill all the threads hanging out there," I thought to myself.

So, I restarted the application, let it do its thing, and then shut it down.

Only to have that phantom thread still left out there, eating memory but not really any processor time.

I sighed, scratched my head and pondered things.

And by ponder, I meant I started searching the web for possible answers.

Which I found on an article that blamed this behavior on the .HIDE method of a form.

So, I started scrolling through my code.

After, scrolling through the first 1,500 lines of code in the first file, I hit CTRL+F and searched for .Hide.

Which I found in the form of a dialog window that was responsible for re-calibrating data after a hard crash. This little guy was being created when I created my primary form, and thus would rarely get shown. Yet, that being the root cause (based on the 'HIDE' thought) didn't make sense to me. After all, it was a child of the primary form. When the primary form was closed, it should have received the notification and disposed of itself like all the rest of the form's children.

So, I created break point in my primary window's CLOSING event, and then launched, let it do its thing for a bit, and then shut it down. My break point hit, and I started stepping through the code.

And I made it all the way through the Closing event handler without a problem.

And then my form was gone.

I was amazed. Aghast even.

After all, I had never removed the Environment.Exit command from the application's Exit event handler, and while stepping through the debugger I had never made it to the application's Exit event handler.

In fact, Visual Studio was reporting that I was STILL debugging.

And then a timer elapsed.

To think, I would probably still be banging my head against the closest hard surface if not for that simple little timer.

But, the timer fired, and I found myself in the code for the stupid dialog window.

Which is when I remembered that I was dealing with WPF, and that my application was set up to not register itself as being closed until every form it owned was closed.

So, I retrace the life cycle for this modal window, and realized that even though it doesn't always get SHOWN, it is always getting created, but is never getting destroyed.

And the Framework is just assuming that you know what you're doing when you don't destroy windows, and is not going to clean those things up for you, regardless of who you believe is the form's parent.

So, anyways, after making sure that that happy little modal window received a call to its .CLOSE method, I recompiled the application, let it do its thing for a bit, and then shut it down.

And I'm happy to report that there were no phantom threads.

No comments:

Blog Widget by LinkWithin