A Day in the Life

A day in my life. Thoughts on leadership, management, startups, technology, software, concurrent development, etc... Basically the stuff I think about from 10am to 6pm.


OnUserPreferenceChanged Hang

You think your code is clean and all is well in the world when all of a sudden your users are starting to report that your application is hanging. You research the issue and discover that from time to time your application hangs when it receives a WM_SETTINGCHANGE message or an OnUserPreferenceChanged event. Ivan Krivyakov did a very thorough write up of what's happening which you can find here. And Microsoft is supposed to have a knowledgebase article out soon about this.

We just ran into this problem and we have learned a few things beyond what Ivan presented. First…did you know that in .NET 2.0 when you create a Control or Form (window) object…it really doesn't exist? For performance reasons Microsoft delays the actual window creation until the window becomes visible or a handle request is made. On the surface this looks innocent enough but if you took the time to read Ivan's report you realize that the final action of window creation may NOT happen on the main UI thread. Where you probably started it.

And don't think you're going to get a CrossThreadException on this one. Even with the CheckForIllegalCrossThreadCalls flag set no exception was thrown. Nor was an exception thrown when the application encountered this problem outside the debugger. Which the documentation says should happen.

Ivan's Freezer code worked very well (he has a link on The Page for some code, you'll want that) and made it very easy for me to reproduce the problem. So step one whenever debugging something like this is to reproduce it on the developer's machine. Freezer enables just that.

Force Window Creation

One thing that Ivan's write-up didn't make clear to me was how to get around this problem. I have confirmed from the Microsoft support guy (Trevor) that the code below will work if you've identified the correct window.

You basically have to force the window creation and you can do that in one of two ways: 1) make the window visible with a Show() or 2) request the Handle. I used the code below:

List lstHandles = new List();
IntPtr hTemp;
foreach (Control myCtrl in Controls)
hTemp = myCtrl.Handle;

hTemp = Handle;

I put the handles in a temporary buffer because I wasn't sure if the optimizing compiler would drop a simple assignment loop like this:

IntPtr hTemp;
foreach (Control myCtrl in Controls)
hTemp = myCtrl.Handle;

hTemp = Handle;

Identifying the Hanging Window

We had not correctly identified the problem Window. To do that we needed Trevor's suggestion which sent us down the correct road. Trevor suggested using Spy++ to identify what threads our windows were running on.

This was a new use case for me (with Spy++), I had already thrown Spy++ out as a tool for this problem because with Spy++ running the hang hung my entire desktop and nothing worked but the good old three finger salute, to get a Task Manager up.

The trick was to not start Spy++ until we were ready to run the test. So I got my application to the area I knew would hang (with the help of Freezer), and then started Spy++. Once in Spy++ you'll want to do the steps below to find that "bad" window:

1. Select Spy->Processes
2. In the Process dialog find your process
3. Expand your process
4. Expand the threads with the + sign
5. Look for GUI elements on those threads. If you find an element on a non-UI thread...you have found culprit.

You might think that it would be obvious and clear where all your windows are but that wasn't the case for us. Down in our audio code an engineer had created a window to pass to the SetCooperativeLevel(). This was the problem window. A window with no title…so we basically stepped through the process until we saw that Spy++ now contained a window on a non-UI thread.

What was interesting here is that the Window was actually created much earlier but only finished being created on the call to SetCooperativeLevel(). So once we discovered where the system thought the window was created we had to back up the callstack to find the actual window creation location.


At December 11, 2007 1:24 AM, Anonymous Zaletskiy said...

It is very interesting notes for me. Thanks.

At May 08, 2008 9:38 AM, Blogger Ray said...

Thanks Kim. You have saved me!

After three weeks of struggling with this exact problem I finally googled onto your blog with the link to Ivan's description.

We recently upgraded our VB.Net solution from .Net Framework 1.1 to .Net Framework 2.0 (XP Pro w/SP1)and our Dell Precision 360 (hyperthreading) P4 3.2 GHz, 1 Gig Ram machines started experiencing this bug. I tried applying the SP1 to the 2.0 Framework because of ( KB945757 - 924895 ) but that didn't help.

Our Dell Precision 380s (P4 3.6GHz, 2 Gig RAM) didn't experience the bug. I guess they were fast to get a handle attached to the MainUI before InvokeRequired ran. We tried attaching the Visual Studio debugger and found that Windows still thought the app was running after coming back from locking the screen.

I finally found the Microsoft KB 943139 article which obliquely referred to the OnUserPreferenceChanged event(http://support.microsoft.com/default.aspx?scid=kb;en-us;943139)

Then a search for that led to your blog. After 3 weeks of banging our head against the wall, finally some relief.

At September 18, 2008 4:13 PM, Blogger Alex said...

All I can say is Thank you, Thank you, Thank you, Thank you, Thank you...

I'm developing a .NET 2.0 client side application that heavily utilizes threading and I started bumping against this exact issue.

Well, suffice it to say, you saved the day here too.

At November 14, 2008 12:06 PM, Anonymous Jonas said...

Your post finally led me on the right track for this problem. Thanks!

At April 28, 2009 11:42 AM, Anonymous Anonymous said...

See my comments on the details of debugging procedure for this at

At May 06, 2009 1:42 PM, Anonymous Anonymous said...

Yes! Thanks for the info, very helpful.

I would add that using the PlaySound API call seems to create a "MCI Command Handling Window" which apparently can cause the app to hang in a similar fashion (Spy++ showed this; the thread assigned to the MCI window does NOT appear in the Threads list in VS 2005 debugger)

At May 06, 2009 3:37 PM, Anonymous Anonymous said...

unfortunately, the MCI windows is not the cause of my freezing problem - or at least it is not the only cause

At May 20, 2009 6:39 AM, Anonymous dnm said...

I just chased my tail for a week on this problem. I created a splash screen on the non-UI thread, on a local variable in the function, then called .Hide(), .Close(), .Dispose() and the variable goes out of scope at the function return. Spy++ doesn't show the splash screen after it's gone. I get the OnUserPreferenceChanged event about an hour later (the program was just sitting idle) and it hangs with the exact call stack described (UI thread doing an invoke onto the thread that created the splash screen). I wonder how to get rid of a form, if the above method doesn't work. Thanks for the article and comments you posted elsewhere. It helped alot.

At June 03, 2009 7:35 AM, Blogger Justin said...

Where can I find more about the MCI command handling window. It is stealing focus from our app (which runs on the winlogon desktop). Who creates it, and why does it steal focus from other windows?

I found one other comment somewhere on the web about it being briefly visible via alt-tab app switching (we see this too, then after 2 alt-tabs it disappears).

At June 03, 2009 7:52 AM, Blogger Kim Greenlee said...

Hi Justin,

I haven't run into that problem. The best thing to do is to find the relevant group on the Microsoft site and post a question to it.

Look for an active newsgroup or forum.



At September 07, 2010 6:42 AM, Anonymous Sildenafil said...

hi Kim! I have been having some problems with a new application I was developing and some people reported me that my app was hanging, I will try these codes and solve the problems hopefully, thank you!


Post a Comment

Links to this post:

Create a Link

<< Home