PC Review


Reply
Thread Tools Rate Thread

Application.DoEvents behavior (Bug?)

 
 
Armin Zingler
Guest
Posts: n/a
 
      5th Aug 2003
Hi again, (VB 2003, Framework 1.1)

the behavior of Application.DoEvents depends on the fact whether the form is
shown modeless or modally. Here is a short sample to reproduce:

1. Create a new WindowsApplication and add a Button to the Form
2. Add this code to the Form:

Shared Sub main()
Dim f As New Form1
'f.ShowDialog()
Application.Run(f)
End Sub

Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click

Do
Application.DoEvents()
Loop While Me.IsHandleCreated
End Sub

3. Start and click the button
4. Click the X to close the form

Everything works as expected. Now replace "Application.Run(f)" by
"f.Showdialog()". Start again and click the button. Now, the Form can not be
closed by clicking the X anymore! My short question is: Why?

One bug you'll also see: The first time when you try to click the X, the
Button is actually clicked!

--
Armin

 
Reply With Quote
 
 
 
 
Joe White
Guest
Posts: n/a
 
      6th Aug 2003
ShowDialog() actually contains its own message loop, similar to the one
you've got in your Button1_Click. Sort of like this:

Public Sub ShowDialog()
SetModalFlagsAndShow()
Do
Application.DoEvents()
While Not Closed
CloseFormAndDestroyWindowHandle()
End Sub

That's a horrific oversimplification (take a look at the code in
Reflector if you're really curious), but that's the gist of it.

So, when you call ShowDialog, it starts its message loop. Then your
Button1_Click method starts another message loop. You never return
control to ShowDialog(), so it never gets around to destroying the
window handle -- which is what your Button1_Click is testing for, so
Button1_Click never terminates either.

A modal window doesn't get to finish "closing" until its ShowDialog
modal loop ends. This is unlike a non-modal window, where the window
handle is destroyed more-or-less as soon as you close the window.
That's why your code (loop until handle destroyed) works in one case and
not the other.

As far as how to fix it... I could tell you how to fix this in Delphi,
but I haven't spent that much time in the guts of Windows.Forms yet. My
first recommendation would be not to do what you're doing -- don't loop
until the form closes, at least not when the form will be shown modally.
Consider using a timer or a thread, to do your background work,
instead of a message loop.

If you have to do what you're doing, then try looping until
Form.DialogResult <> DialogResult.None (untested, but I'm guessing it
should work) -- but be aware that this will only work for a form that
was shown with ShowDialog(), and not one shown with Show().

Or, you could check the form's Modal property. If Modal is True, then
use DialogResult as your exit condition. If Modal is False, then use
IsHandleCreated.


Armin Zingler wrote:
> Hi again, (VB 2003, Framework 1.1)
>
> the behavior of Application.DoEvents depends on the fact whether the form is
> shown modeless or modally. Here is a short sample to reproduce:
>
> 1. Create a new WindowsApplication and add a Button to the Form
> 2. Add this code to the Form:
>
> Shared Sub main()
> Dim f As New Form1
> 'f.ShowDialog()
> Application.Run(f)
> End Sub
>
> Private Sub Button1_Click( _
> ByVal sender As System.Object, _
> ByVal e As System.EventArgs) _
> Handles Button1.Click
>
> Do
> Application.DoEvents()
> Loop While Me.IsHandleCreated
> End Sub
>
> 3. Start and click the button
> 4. Click the X to close the form
>
> Everything works as expected. Now replace "Application.Run(f)" by
> "f.Showdialog()". Start again and click the button. Now, the Form can not be
> closed by clicking the X anymore! My short question is: Why?
>
> One bug you'll also see: The first time when you try to click the X, the
> Button is actually clicked!


 
Reply With Quote
 
Armin Zingler
Guest
Posts: n/a
 
      6th Aug 2003
Joe, thanks for your reply. More inline.

"Joe White" <(E-Mail Removed)> schrieb
> ShowDialog() actually contains its own message loop, similar to the
> one you've got in your Button1_Click. Sort of like this:
>
> Public Sub ShowDialog()
> SetModalFlagsAndShow()
> Do
> Application.DoEvents()
> While Not Closed
> CloseFormAndDestroyWindowHandle()
> End Sub
>
> That's a horrific oversimplification (take a look at the code in
> Reflector if you're really curious), but that's the gist of it.
>
> So, when you call ShowDialog, it starts its message loop. Then your
> Button1_Click method starts another message loop.



So far I knew.


> You never return
>
> control to ShowDialog(), so it never gets around to destroying the
> window handle -- which is what your Button1_Click is testing for, so
>
> Button1_Click never terminates either.
>
> A modal window doesn't get to finish "closing" until its ShowDialog
> modal loop ends. This is unlike a non-modal window, where the
> window
> handle is destroyed more-or-less as soon as you close the window.
> That's why your code (loop until handle destroyed) works in one case
> and not the other.


I don't understand this: When I click the X, WM_CLOSE is sent. This is true
for modeless and modal Forms (I checked it by overriding WndProc). WM_CLOSE
should close the window (DestroyWindow). This happens when calling
Application.DoEvents. After calling DoEvents, the handle should have been
destroyed. Why is there a difference between modeless and modal forms?
Application.Run is a message loop just like ShowDialog, so I don't see the
difference.

Why does ShowDialog interpret WM_CLOSE and destroys the window? I thought
it's the window procedure that catches wm_close and destroys itself, and the
window procedure is the same in modal and modeless forms.

I'm not sure whether the same thing happens when showing a modal form in
VB6. There, I can show it modally and I am still able to close it during
the loop containing doevents.

> As far as how to fix it... I could tell you how to fix this in
> Delphi, but I haven't spent that much time in the guts of
> Windows.Forms yet. My first recommendation would be not to do what
> you're doing -- don't loop until the form closes, at least not when
> the form will be shown modally.
> Consider using a timer or a thread, to do your background work,
> instead of a message loop.
>
> If you have to do what you're doing, then try looping until
> Form.DialogResult <> DialogResult.None (untested, but I'm guessing it
> should work) -- but be aware that this will only work for a form
> that
> was shown with ShowDialog(), and not one shown with Show().
>
> Or, you could check the form's Modal property. If Modal is True,
> then use DialogResult as your exit condition. If Modal is False,
> then use IsHandleCreated.


The example is not "real-world" code. I only came across this issue doing
something else, so there is no need to find a solution. I was just curious
why it doesn't work as expected. I'm afraid but I still didn't get it. :-/


--
Armin

 
Reply With Quote
 
Joe White
Guest
Posts: n/a
 
      6th Aug 2003
> I don't understand this: When I click the X, WM_CLOSE is sent. This is true
> for modeless and modal Forms (I checked it by overriding WndProc). WM_CLOSE
> should close the window (DestroyWindow). This happens when calling
> Application.DoEvents. After calling DoEvents, the handle should have been
> destroyed. Why is there a difference between modeless and modal forms?


Yes, WM_CLOSE is sent. However, AFAIK, if the form is modal, then
WM_CLOSE sets ModalResult instead of immediately destroying the window
handle.


> Application.Run is a message loop just like ShowDialog, so I don't see the
> difference.


Well, I'm not totally sure. But keep in mind that Application.Run has
totally arbitrary exit conditions, which aren't even specified by the
Application class -- IIRC, it keeps looping until someone (usually the
ApplicationContext) calls ThreadTerminate.

Form.ShowDialog has very specific exit conditions: it exits when the
form's DialogResult changes to something other than DialogResult.None.
It's actually not that simple -- the code is a tangled maze about six
methods deep -- so there may be other factors.

So there's not necessarily an automatic reason why Application.Run
should behave the same as Form.ShowDialog. Yes, it might be nice if
they were similar, but I suspect there are a lot of details in the way.


> Why does ShowDialog interpret WM_CLOSE and destroys the window? I thought
> it's the window procedure that catches wm_close and destroys itself, and the
> window procedure is the same in modal and modeless forms.


You're asking for a little more depth of knowledge than I've got, but
I'll hazard some guesses. First, I don't think ShowDialog processes
WM_CLOSE; I think the Form processes WM_CLOSE, but uses it to set
DialogResult instead of destroying its window handle immediately. (I
haven't confirmed this, but the code that's invoked as a result of
ShowDialog is a real mess, and aside from your questions I haven't had
any reasons to dive too far into it.)

A non-modal form *has* to destroy its own window handle, because there's
nobody else to clean up after it. A modal form *can* wait for someone
else (ShowDialog) to clean up after it, but exactly why they chose to
make it work that way, I'm not sure. I do have some guesses. In
particular, ShowDialog sets up some application state (disabling all
other forms) -- and if resetting that state is non-trivial, it would
make more sense for ShowDialog to do it. ShowDialog probably has local
variables that store various bits of initial state, and it's cleaner to
have ShowDialog restore that state than to somehow pass it into the
Form. And it's plausible enough that some of that state-restoration
could require the form's window handle still exist at the time.

But bottom line, I'm not sure. I guess I haven't had any reason to
worry about it too much yet. <grin> If you don't have Lutz Roeder's
Reflector yet, download it and take a look at the ShowDialog code.

 
Reply With Quote
 
Armin Zingler
Guest
Posts: n/a
 
      7th Aug 2003
You're absolutely right. I had a look at the docs of ShowDialog (why not
earlier?) and there are also some words describing this behavior. I also did
some IL debugging confirming everything.

Anyway, it is like it is - and now I know how it is. ;-)

Thanks for your participation!


--
Armin


"Joe White" <(E-Mail Removed)> schrieb
> > I don't understand this: When I click the X, WM_CLOSE is sent. This
> > is true for modeless and modal Forms (I checked it by overriding
> > WndProc). WM_CLOSE should close the window (DestroyWindow). This
> > happens when calling Application.DoEvents. After calling DoEvents,
> > the handle should have been destroyed. Why is there a difference
> > between modeless and modal forms?

>
> Yes, WM_CLOSE is sent. However, AFAIK, if the form is modal, then
> WM_CLOSE sets ModalResult instead of immediately destroying the
> window
> handle.
>
> > Application.Run is a message loop just like ShowDialog, so I don't
> > see the difference.

>
> Well, I'm not totally sure. But keep in mind that Application.Run
> has totally arbitrary exit conditions, which aren't even specified
> by the Application class -- IIRC, it keeps looping until someone
> (usually the ApplicationContext) calls ThreadTerminate.
>
> Form.ShowDialog has very specific exit conditions: it exits when the
> form's DialogResult changes to something other than
> DialogResult.None.
> It's actually not that simple -- the code is a tangled maze about six
> methods deep -- so there may be other factors.
>
> So there's not necessarily an automatic reason why Application.Run
> should behave the same as Form.ShowDialog. Yes, it might be nice if
>
> they were similar, but I suspect there are a lot of details in the
> way.
>
>
> > Why does ShowDialog interpret WM_CLOSE and destroys the window? I
> > thought it's the window procedure that catches wm_close and
> > destroys itself, and the window procedure is the same in modal and
> > modeless forms.

>
> You're asking for a little more depth of knowledge than I've got, but
> I'll hazard some guesses. First, I don't think ShowDialog processes
>
> WM_CLOSE; I think the Form processes WM_CLOSE, but uses it to set
> DialogResult instead of destroying its window handle immediately. (I
> haven't confirmed this, but the code that's invoked as a result of
>
> ShowDialog is a real mess, and aside from your questions I haven't
> had any reasons to dive too far into it.)
>
> A non-modal form *has* to destroy its own window handle, because
> there's nobody else to clean up after it. A modal form *can* wait
> for someone else (ShowDialog) to clean up after it, but exactly why
> they chose to make it work that way, I'm not sure. I do have some
> guesses. In particular, ShowDialog sets up some application state
> (disabling all other forms) -- and if resetting that state is
> non-trivial, it would make more sense for ShowDialog to do it.
> ShowDialog probably has local variables that store various bits of
> initial state, and it's cleaner to have ShowDialog restore that
> state than to somehow pass it into the Form. And it's plausible
> enough that some of that state-restoration could require the form's
> window handle still exist at the time.
>
> But bottom line, I'm not sure. I guess I haven't had any reason to
> worry about it too much yet. <grin> If you don't have Lutz
> Roeder's
> Reflector yet, download it and take a look at the ShowDialog code.


 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Weird behavior, Program bombs out unless Application.DoEvents() or GC.Collect is called. Hasani Microsoft C# .NET 3 8th Aug 2004 06:16 AM
subtle issue with Application.DoEvents() behavior Wiktor Zychla Microsoft C# .NET 1 27th Mar 2004 04:09 PM
subtle issue with Application.DoEvents() behavior Wiktor Zychla Microsoft Dot NET Framework Forms 1 27th Mar 2004 04:09 PM
NotifyIcon Bug, Application.EnableVisualStyles, Application.DoEvents, and Memory Leaks Michael S. Malley Microsoft C# .NET 0 10th Mar 2004 09:52 PM
NotifyIcon Bug, Application.EnableVisualStyles, Application.DoEvents, and Memory Leaks Michael S. Malley Microsoft Dot NET 0 10th Mar 2004 09:52 PM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 11:50 AM.