main form, IDisposable, and Application.Run

  • Thread starter Thread starter Ben Voigt [C++ MVP]
  • Start date Start date
B

Ben Voigt [C++ MVP]

Does anyone else think it is a bug that the new project wizard generates the
following in the Main() method?

Application.Run(new MyFormClass());

Shouldn't it be

using (MyFormClass mainForm = new MyFormClass)
Application.Run(mainForm);

instead?
 
Does anyone else think it is a bug that the new project wizard generates
the
following in the Main() method?

Application.Run(new MyFormClass());

Shouldn't it be

using (MyFormClass mainForm = new MyFormClass)
Application.Run(mainForm);

instead?

No, it shouldn't. For non-modal forms (those shown with the Form.Show()
method), they are disposed when they are closed. You only need to dispose
modal forms (those shown with Form.ShowDialog()).

Pete
 
Ben Voigt said:
Does anyone else think it is a bug that the new project wizard generates the
following in the Main() method?

Application.Run(new MyFormClass());

Shouldn't it be

using (MyFormClass mainForm = new MyFormClass)
Application.Run(mainForm);

instead?

Well, the docs for Application.Run specify:

<quote>
The Dispose method of the Form class will be called prior to the return
of this method.
</quote>

So it's not like the form isn't going to be disposed - unless an
exception is thrown between construction and the method call.

Were you worried that the form wouldn't be disposed, or that it's a
basically ugly and asymmetric situation?
 
Jon said:
Well, the docs for Application.Run specify:

<quote>
The Dispose method of the Form class will be called prior to the
return of this method.
</quote>

So it's not like the form isn't going to be disposed - unless an
exception is thrown between construction and the method call.

I'm beginning to suspect that the Form is finalized, not disposed, if an
exception is thrown anywhere in the message loop. Tracing through the
relevant framework code in Reflector isn't going very well, there's a lot of
it.

Maybe the exception is being thrown from the constructor after all, in which
case a containing using block won't help.
 
[...]
So it's not like the form isn't going to be disposed - unless an
exception is thrown between construction and the method call.

I'm beginning to suspect that the Form is finalized, not disposed, if an
exception is thrown anywhere in the message loop.

I haven't looked at the Application.Run() method via Reflector (or any
other for that matter :) ), but given the documentation, it seems
reasonable to believe that it deals with disposing of form instances even
in the exceptional case.

I know that exceptions are dealt with differently in the GUI thread than
in other threads. They must be because there's the ThreadException event
that you can subscribe to, and if there's a handler for that event, GUI
thread exceptions that occur during the dispatch of window messages are
simply reported and don't cause the application to exit.

It seems plausible (you could check this in Reflector, I guess :) ) that
when the event is _not_ handle, the way the application exits is either
through rethrowing the exception (in which case a "using" or
try/catch/finally block in the Run() method or something it calls would
close and/or dispose the form), or by explicitly closing all open forms
(which would obviously dispose the form).

And even if not, since the project wizard outputs code that will simply
exit the application once the Run() method returns, it's difficult to see
why the form should be required to be disposed at that point anyway.
Personally, I am generally in the habit of cleaning resources up before
exiting. However, there's a good argument in favor of just not
bothering...the OS will clean them up when the process exits anyway, and
it just delays the exit of your process to clean stuff up that is in use.
Tracing through the
relevant framework code in Reflector isn't going very well, there's a
lot of
it.

Maybe the exception is being thrown from the constructor after all, in
which
case a containing using block won't help.

Isn't it a requirement that if the constructor throws an exception, any
unmanaged resources in the class are to be disposed of by the class itself?

I'm curious: is there some specific scenario you're running into that
prompts the question? Are you seeing a form not get disposed when it's a
problem for it not to be? If so, how is that happening?

Pete
 
Peter said:
[...]
So it's not like the form isn't going to be disposed - unless an
exception is thrown between construction and the method call.

I'm beginning to suspect that the Form is finalized, not disposed,
if an exception is thrown anywhere in the message loop.

I haven't looked at the Application.Run() method via Reflector (or any
other for that matter :) ), but given the documentation, it seems
reasonable to believe that it deals with disposing of form instances
even in the exceptional case.

I know that exceptions are dealt with differently in the GUI thread
than in other threads. They must be because there's the
ThreadException event that you can subscribe to, and if there's a
handler for that event, GUI thread exceptions that occur during the
dispatch of window messages are simply reported and don't cause the
application to exit.
It seems plausible (you could check this in Reflector, I guess :) )
that when the event is _not_ handle, the way the application exits is
either through rethrowing the exception (in which case a "using" or
try/catch/finally block in the Run() method or something it calls
would close and/or dispose the form), or by explicitly closing all
open forms (which would obviously dispose the form).

And even if not, since the project wizard outputs code that will
simply exit the application once the Run() method returns, it's
difficult to see why the form should be required to be disposed at
that point anyway. Personally, I am generally in the habit of
cleaning resources up before exiting. However, there's a good
argument in favor of just not bothering...the OS will clean them up
when the process exits anyway, and it just delays the exit of your
process to clean stuff up that is in use.
Tracing through the
relevant framework code in Reflector isn't going very well, there's a
lot of
it.

Maybe the exception is being thrown from the constructor after all,
in which
case a containing using block won't help.

Isn't it a requirement that if the constructor throws an exception,
any unmanaged resources in the class are to be disposed of by the
class itself?

Yeah, I think so. I'll have to review the constructor and make sure that
Dispose gets called appropriately on member objects if an exception is
thrown. I wish this class was C++/CLI...
I'm curious: is there some specific scenario you're running into that
prompts the question? Are you seeing a form not get disposed when
it's a problem for it not to be? If so, how is that happening?

I'm getting exception dumps where the call stack begins from
Component.Finalize, which isn't very helpful for understanding the overall
state of the app.
 
Hi Ben,

Thanks for your feedback.

Yes, the MainForm is maintained by the ApplicationContext class, which will
dispose the form in its Dispose() method:

protected virtual void Dispose(bool disposing)
{
if (disposing && (this.mainForm != null))
{
if (!this.mainForm.IsDisposed)
{
this.mainForm.Dispose();
}
this.mainForm = null;
}
}

We did not guard the MainForm contructor with the try...finally block in
wizard template code. If you believe this wizard code should better be
added with the "using", I would recommend you to submit it as a feedback in
the link below:
https://connect.microsoft.com/VisualStudio?wa=wsignin1.0

Regarding the the exception dump debugging, I think you may switch the
context to the GUI thread and examine its stack trace to understand the
application state. Also, if I remember correct, the Winform is STA, so
finalizer thread's calling to the form dispose will be marshalled to the
GUI thread to perform the real calling.

Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
"Jeffrey Tan[MSFT]" said:
Hi Ben,

Thanks for your feedback.

Yes, the MainForm is maintained by the ApplicationContext class, which
will
dispose the form in its Dispose() method:

I couldn't find any using block for the ApplicationContext class either.

But others' replies did point me in the right direction, namely that the
resource leaks (until GC) were a result of abnormal termination of the Form
constructor.
 
Hi Ben,

Yes, if the exception is thrown in the constructor, it sure may cause this
problem.

For the disposition issue, I will check it tomorrow when I come back to
office, thanks for your patient.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Hi Ben,

Sorry for letting you wait.

In the debugger, I set a breakpoint in the ApplicationContext.Dispose()
method, during form closing we can see the stack trace below:
System.Windows.Forms.ApplicationContext.Dispose(bool disposing = true) C#
System.Windows.Forms.Application.ThreadContext.DisposeThreadWindows() C#

System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.Unsaf
eNativeMethods.IMsoComponentManager.FPushMessageLoop(int dwComponentID, int
reason, int pvLoopData) C#
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int
reason = -1, System.Windows.Forms.ApplicationContext context =
{System.Windows.Forms.ApplicationContext}) C#
System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason,
System.Windows.Forms.ApplicationContext context) C#
System.Windows.Forms.Application.Run(System.Windows.Forms.Form mainForm)
C#
DisposeTest.Program.Main() C#

So it is the ThreadContext.DisposeThreadWindows() that calls the
ApplicationContext.Dispose() method which finally disposes the mainForm.

Hope this makes sense to you.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Back
Top