WinForms bug? ThreadException not invoked on Exceptions in system code pre-processing the messages!

B

Brian

In a nutshell:
= Application.Run runs the message pump... pulling messages off the Windows
Message Queue and invoking the appropriate handler.
= If during such a message handler an Exception is thrown that is not
handled inside that handler, then it fires the Application.ThreadException
event. By registering a handler for that event, you can deal with such
unhandled Exceptions gracefully, and prevent your application from dieing
unnecessarily.
= We have setup an Application.ThreadException handler... in testing, it
gets invoked and works great.

However, if an Exception is raised during the PRE-PROCESSING of a Windows
Message (in the System code that is responsible for digesting the message on
the queue and deciding what handler to invoke), then that Exception does NOT
fire the Application.ThreadException event. It also does not fire the
AppDomain.UnhandledException event. Such Exceptions abort Application.Run,
disposing of your MainForm. In other words, "poof" your application is
gone!

I assume Microsoft coded it that way assuming they would never have
unhandled Exceptions in that system code... but they do. For example:

at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.ToolStripDropDown.CreateHandle()
at System.Windows.Forms.Control.get_Handle()
at
System.Windows.Forms.ToolStripManager.ModalMenuFilter.PreFilterMessage(Message&
m)
at System.Windows.Forms.Application.ThreadContext.ProcessFilters(MSG&
msg, Boolean& modified)
at
System.Windows.Forms.Application.ThreadContext.PreTranslateMessage(MSG& msg)
at
System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(MSG&
msg)
at
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at TCPSM.Program.Main() in C:\Documents and Settings\briank\My
Documents\Source\Development\Client\Source\TCPSM\Program.cs:line 220

Note that there is nothing but System.Windows.Forms code in that... nowhere
for me to put a catch (other than outside Application.Run, which is too
late). So, my only hope is the handler on Application.ThreadException...
but it never gets called here. (It was successfully called several times
before this point in the app... so, I know I have it hooked up right and it
works.)

Why is CreateHandle getting an Exception? Because I used another app to
fill the 3MB of Desktop Heap memory with windows so that my app would start
getting the "Error creating window handle." exceptions whenever I try to get
a new handle (or otherwise need Desktop Heap space).

Why am I doing that? To emulate when my users (who also use CAD, CAE, and
lots of other heavy duty tools) run out of Desktop Heap space. Our app used
to just crash; with improvements to our exception handling, it now generally
stays up; until the Exception gets raised in message pre-processing, then we
die as before.

Any suggestions? Is there some other event that I gets raised when such
system code gets an Exception? Is there some flag I need to set?

Or do I need to implement my own version of Application.Run such that I can
properly catch Exceptions, unlike Microsoft's code?
(I am assuming that getting Microsoft to fix their buggy .NET 2.0 code is
not too likely.)
 
J

Jesse Houwing

* Brian wrote, On 7-9-2009 23:56:
In a nutshell:
= Application.Run runs the message pump... pulling messages off the Windows
Message Queue and invoking the appropriate handler.
= If during such a message handler an Exception is thrown that is not
handled inside that handler, then it fires the Application.ThreadException
event. By registering a handler for that event, you can deal with such
unhandled Exceptions gracefully, and prevent your application from dieing
unnecessarily.
= We have setup an Application.ThreadException handler... in testing, it
gets invoked and works great.

However, if an Exception is raised during the PRE-PROCESSING of a Windows
Message (in the System code that is responsible for digesting the message on
the queue and deciding what handler to invoke), then that Exception does NOT
fire the Application.ThreadException event. It also does not fire the
AppDomain.UnhandledException event. Such Exceptions abort Application.Run,
disposing of your MainForm. In other words, "poof" your application is
gone!

I assume Microsoft coded it that way assuming they would never have
unhandled Exceptions in that system code... but they do. For example:

at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.ToolStripDropDown.CreateHandle()
at System.Windows.Forms.Control.get_Handle()
at
System.Windows.Forms.ToolStripManager.ModalMenuFilter.PreFilterMessage(Message&
m)
at System.Windows.Forms.Application.ThreadContext.ProcessFilters(MSG&
msg, Boolean& modified)
at
System.Windows.Forms.Application.ThreadContext.PreTranslateMessage(MSG& msg)
at
System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(MSG&
msg)
at
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at TCPSM.Program.Main() in C:\Documents and Settings\briank\My
Documents\Source\Development\Client\Source\TCPSM\Program.cs:line 220

Note that there is nothing but System.Windows.Forms code in that... nowhere
for me to put a catch (other than outside Application.Run, which is too
late). So, my only hope is the handler on Application.ThreadException...
but it never gets called here. (It was successfully called several times
before this point in the app... so, I know I have it hooked up right and it
works.)

Why is CreateHandle getting an Exception? Because I used another app to
fill the 3MB of Desktop Heap memory with windows so that my app would start
getting the "Error creating window handle." exceptions whenever I try to get
a new handle (or otherwise need Desktop Heap space).

Why am I doing that? To emulate when my users (who also use CAD, CAE, and
lots of other heavy duty tools) run out of Desktop Heap space. Our app used
to just crash; with improvements to our exception handling, it now generally
stays up; until the Exception gets raised in message pre-processing, then we
die as before.

Any suggestions? Is there some other event that I gets raised when such
system code gets an Exception? Is there some flag I need to set?

Or do I need to implement my own version of Application.Run such that I can
properly catch Exceptions, unlike Microsoft's code?
(I am assuming that getting Microsoft to fix their buggy .NET 2.0 code is
not too likely.)

My guess is that a OutOfMemoryExcpetion is thrown here. And because any
handling will probably fail if there is no memory (or another vital
resource), the CLR will terminate the application without attempting to
call the ApplicationException or UnhandledException events. The same
happens for other vital exceptions which may have left the CLR in an
unstable state. This is a security 'feature'. It would be unwise to
attempt running more code after the CLR state has been corrupted by one
of these exceptions.

To solve this gracefully you have a few options. First, you could host
the CLR in an unmanaged application and detect the CLR problems from
there, Create a new CLR instance if the old one fails and then continue
as if nothing has happened.

Or you can create a watchdog process that runs parallel to your main
application and which will detect that your application has crashed and
will restart it if needed, presenting the user with a polite message
explaining what just happened.

A third option that you could venture into, but in which I have no
experience in, so this might not work, is to monitor the system
resources and gracefully shut down your app just before a critical
condition is reached. So if you're going to run out of handles, save all
your state, and close as many resources as possible.
 
B

Brian

Jesse Houwing said:
My guess is that a OutOfMemoryExcpetion is thrown here.

No, it is NOT out of memory... just out of Desktop Heap (which is limited to
just a few MB even when you have several GB of RAM).
The Exception thrown is "Error creating Window Handle."... same Exception
you'd get if you hit the 10,000 handle limit... but we're not close to that
either.

So, the CLR is fat and happy... and really, Application.Run and its message
pump should be too, except that for some reason it needs to CreateHandle to
pre-process messages... no clue why it needs a handle, but it does (and I
clearly cannot fix that).

Note that I recover just fine from this Exception as long as I don't hit it
during Windows message pre-processing. Soooo, if it would just fire the
ThreadException event during pre-processing exceptions, so that I could
handle the Exception like I do everywhere else, all would be well.

A third option that you could venture into, but in which I have no
experience in, so this might not work, is to monitor the system resources
and gracefully shut down your app just before a critical condition is
reached. So if you're going to run out of handles, save all your state,
and close as many resources as possible.


Yeah, if we can't catch the Exception somehow, the next best thing would be
to incessantly check the consumption of Desktop Heap and Handles and tell
the user when they are low on resources... unfortunately, Microsoft gives us
no way (that I have been able to find) to check the amount of Desktop Heap
remaining. (MS documentation asserts that there is no way to check... but
if someone knows, we'd be interested.)

Thanks,

Brian
 
J

Jesse Houwing

* Brian wrote, On 9-9-2009 6:45:
No, it is NOT out of memory... just out of Desktop Heap (which is limited to
just a few MB even when you have several GB of RAM).
The Exception thrown is "Error creating Window Handle."... same Exception
you'd get if you hit the 10,000 handle limit... but we're not close to that
either.

What is the type of exception being thrown then. Not the message, but
the type?

There are a few others on the net with this problem:
http://copenhagens.blogspot.com/2007/09/desktop-heap-and-gdi-objects-usage-and.html

http://www.winvistatips.com/re-desktop-heap-csrsrvsharedsectionbase-invalid-t533131.html

Using the WMI Code Generator I found a number of WMI classes you might
be interested in Root\WMI\HEapFree and Root\WMI\HeapAlloc
 
B

Brian

Jesse Houwing said:
* Brian wrote, On 9-9-2009 6:45:

What is the type of exception being thrown then. Not the message, but the
type?

System.ComponentModel.Win32Exception



There are a few others on the net with this problem:
http://copenhagens.blogspot.com/2007/09/desktop-heap-and-gdi-objects-usage-and.html


Yep, that is the problem I am trying to handle gracefully.
Yes, you can alleviate the problem a bit by changing that registry setting
from the default of 3MB up to 8MB, as they show.
However, even 12MB won't eliminate the possibility of hitting that limit
(and you can't go much about 12MB because
there's an absolute limit of 48MB total and 12MB is per desktop and you have
at least 3 desktops).


Brian
 
M

miher

Brian said:
In a nutshell:
= Application.Run runs the message pump... pulling messages off the
Windows Message Queue and invoking the appropriate handler.
= If during such a message handler an Exception is thrown that is not
handled inside that handler, then it fires the Application.ThreadException
event. By registering a handler for that event, you can deal with such
unhandled Exceptions gracefully, and prevent your application from dieing
unnecessarily.
= We have setup an Application.ThreadException handler... in testing, it
gets invoked and works great.

However, if an Exception is raised during the PRE-PROCESSING of a Windows
Message (in the System code that is responsible for digesting the message
on the queue and deciding what handler to invoke), then that Exception
does NOT fire the Application.ThreadException event. It also does not
fire the AppDomain.UnhandledException event. Such Exceptions abort
Application.Run, disposing of your MainForm. In other words, "poof" your
application is gone!

I assume Microsoft coded it that way assuming they would never have
unhandled Exceptions in that system code... but they do. For example:

at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.ToolStripDropDown.CreateHandle()
at System.Windows.Forms.Control.get_Handle()
at
System.Windows.Forms.ToolStripManager.ModalMenuFilter.PreFilterMessage(Message&
m)
at System.Windows.Forms.Application.ThreadContext.ProcessFilters(MSG&
msg, Boolean& modified)
at
System.Windows.Forms.Application.ThreadContext.PreTranslateMessage(MSG&
msg)
at
System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(MSG&
msg)
at
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at TCPSM.Program.Main() in C:\Documents and Settings\briank\My
Documents\Source\Development\Client\Source\TCPSM\Program.cs:line 220

Note that there is nothing but System.Windows.Forms code in that...
nowhere for me to put a catch (other than outside Application.Run, which
is too late). So, my only hope is the handler on
Application.ThreadException... but it never gets called here. (It was
successfully called several times before this point in the app... so, I
know I have it hooked up right and it works.)

Why is CreateHandle getting an Exception? Because I used another app to
fill the 3MB of Desktop Heap memory with windows so that my app would
start getting the "Error creating window handle." exceptions whenever I
try to get a new handle (or otherwise need Desktop Heap space).

Why am I doing that? To emulate when my users (who also use CAD, CAE, and
lots of other heavy duty tools) run out of Desktop Heap space. Our app
used to just crash; with improvements to our exception handling, it now
generally stays up; until the Exception gets raised in message
pre-processing, then we die as before.

Any suggestions? Is there some other event that I gets raised when such
system code gets an Exception? Is there some flag I need to set?

Or do I need to implement my own version of Application.Run such that I
can properly catch Exceptions, unlike Microsoft's code?
(I am assuming that getting Microsoft to fix their buggy .NET 2.0 code is
not too likely.)


Hi,

I can't say much without proper code that shows Your problem, but can You
please try using the same construct (if You have't done already), that is in
the example here :
http://msdn.microsoft.com/en-us/lib...ms.application.setunhandledexceptionmode.aspx

-Zsolt
 
B

Brian

miher said:
I can't say much without proper code that shows Your problem, but can You
please try using the same construct (if You have't done already), that is
in the example here :
http://msdn.microsoft.com/en-us/lib...ms.application.setunhandledexceptionmode.aspx


Yeah, I tried adding that ... setting it to
UnhandledExceptionMode.CatchException ... had no impact.

Note that that mode setting seems to just change whether or not it pays
attention to the user's configuration file.
It does NOT change what it does in different parts of your code. So, in
this case, where it is definitely catching
things in most parts of the code, I don't think this should have any effect.
BUT, I did try it anyway... just in case.
No effect.

Thanks,

Brian
 
M

miher

Brian said:
Yeah, I tried adding that ... setting it to
UnhandledExceptionMode.CatchException ... had no impact.

Note that that mode setting seems to just change whether or not it pays
attention to the user's configuration file.
It does NOT change what it does in different parts of your code. So, in
this case, where it is definitely catching
things in most parts of the code, I don't think this should have any
effect. BUT, I did try it anyway... just in case.
No effect.

Thanks,

Brian

Hi,

Do You have time to create and post a (desirably small) code example that
shows Your problem ?

-Zsolt
 
B

Brian

miher said:
Do You have time to create and post a (desirably small) code example that
shows Your problem ?

It's easy to whip up something that will reliably fill Desktop Heap memory.
The trick is figuring out how to make the Windows Messaging pre-processing
stuff to try to create a handle.
I'm not sure why it would ever do that... I just know that sometimes it
does... so, its a little hard to figure out how to make it do it.
The trick was adding a ToolStripDropDown... when you click on that... poof,
your app is dead.
The prefiltering on that control tries to CreateHandle for some reason...
and it has no memory to do that, so it throws.
But Windows doesn't expect anything to throw in its prefiltering code... it
doesn't invoke the Event Handler as it does elsewhere.

You can get the project here:
http://dl.getdropbox.com/u/1987084/DesktopHeapKiller.zip

Run the app.
Click on "Say Hi!" to see that it raises a Message Box.
Click on Fill Desktop Heap button... if you have the default size 3MB heap,
it should fill it up in the 8000's.
Once its full, you'll see an Exception appear in the Output window of Visual
Studio.
Click on "Say Hi!" again to see that it can't raise that Message Box any
more... no Desktop Heap space.
Click on anything else but the toolstrip pulldown button... you may get some
Exceptions, but the app keeps running.
Now try clicking on the toolstrip pull down button. Poof! Note the debug
output.

We should never get here!
Exception = System.ComponentModel.Win32Exception: The system cannot find the
file specified
at System.Windows.Forms.NativeWindow.WindowClass.RegisterClass()
at System.Windows.Forms.NativeWindow.WindowClass.Create(String className,
Int32 classStyle)
at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.ToolStripDropDown.CreateHandle()
at System.Windows.Forms.Control.get_Handle()
at
System.Windows.Forms.ToolStripManager.ModalMenuFilter.PreFilterMessage(Message&
m)
at System.Windows.Forms.Application.ThreadContext.ProcessFilters(MSG& msg,
Boolean& modified)
at System.Windows.Forms.Application.ThreadContext.PreTranslateMessage(MSG&
msg)
at
System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(MSG&
msg)
at
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at DesktopHeapKiller.Program.Main()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top