InvokeRequired/BeginInvoke() on Disposed control?

D

David Sworder

Hi,

I have a form that displays data (is that vague enough for you?). The
data comes in on a thread-pool thread. Since the thread pool thread is not
the same as the UI thread, the callback function of my form follows the
standard design pattern:

if(IsDisposed){
return;
}
if(InvokeRequired){
this.BeginInvoke(delegateToThisMethod,new Object[]{args...});
return;
}
....painting code here.....

The idea here is: If the user has closed the form (it's disposed), then
just ignore the incoming data. If the form is still open, show the data via
some painting code, but do so on the form's UI thread. Nothing too unusual
here. My question is: What happens if BeginInvoke() is called on a form that
is already disposed? This could happen in the following situation:

In the code above, let's say that the this.BeingInvoke() call is just
about to execute, but then a context switch happens and the user's request
to close the window transpires which leads to a Dispose() call on the form.
The context then switches back to my BeginInvoke() call which would then be
made against a disposed form. Is this a problem? From my perspective, I
don't care since the 'IsDisposed' check at the top of my code should catch
this... but will WinForms internally have a problem with the fact that
BeginInvoke() is being called on a form whose window handle has been
disposed?
 
N

Nicholas Paldino [.NET/C# MVP]

David,

I don't believe this will be a problem. Basically, during a call to
Invoke/BeginInvoke, a message is sent to a window (using a window handle).
Now, that window can be your form's window, or it can be another one. This
is dependent on the control that Invoke is called on. If the form is
disposed, then another window will be used.

While you could check on the call before BeginInvoke to see if the form
is disposed, you should also call in the method that is passed to
BeginInvoke as well, when you are on the thread that an Invoke is not
required on. If you are disposed there, then you should not call the rest
of your code, and then exit.

Hope this helps.
 
T

TT \(Tom Tempelaere\)

Hi,

David Sworder said:
Hi,

I have a form that displays data (is that vague enough for you?). The
data comes in on a thread-pool thread. Since the thread pool thread is not
the same as the UI thread, the callback function of my form follows the
standard design pattern:

if(IsDisposed){
return;
}
if(InvokeRequired){
this.BeginInvoke(delegateToThisMethod,new Object[]{args...});
return;
}
...painting code here.....

The idea here is: If the user has closed the form (it's disposed), then
just ignore the incoming data. If the form is still open, show the data via
some painting code, but do so on the form's UI thread. Nothing too unusual
here. My question is: What happens if BeginInvoke() is called on a form that
is already disposed? This could happen in the following situation:

You will get a runtime exception, stating that Invoke is called on an
already disposed object.

By the way, I would do the painting in OnPaint, and just call Ivalidate if
the data arrives (which your callback signals). Invalidate triggers the
painting.
In the code above, let's say that the this.BeingInvoke() call is just
about to execute, but then a context switch happens and the user's request
to close the window transpires which leads to a Dispose() call on the form.
The context then switches back to my BeginInvoke() call which would then be
made against a disposed form. Is this a problem? From my perspective, I [...]

This could lead to problems. I had this problem myself in a similar setting.
I solved it by keeping a flag to indicate whether the form is closing.
Initialize the flag to false.Make a lock object for the closing flag. In the
OnClosing handler, begin with this:

lock( closingLock )
{
isClosing = true;
}
// ... other closing code

In the callback method, execute everything guarded by the closingLock (which
is nothing more than a mutex).

lock( closingLock )
{
if( ! isClosing )
this.BeginInvoke(delegateToThisMethod, new Object[]{args...});
}

If you follow my advice, you invoke Invalidate instead of your painting
handler, and have the OnPaint handler paint based on the available data.

Cheers,
 
D

David Sworder

Now, that window can be your form's window, or it can be another one.
This
is dependent on the control that Invoke is called on. If the form is
disposed, then another window will be used.

Another window? Which one?

David
 
T

TT \(Tom Tempelaere\)

message [...]
if(IsDisposed){
return;
}
if(InvokeRequired){
this.BeginInvoke(delegateToThisMethod,new Object[]{args...});
return;
}
...painting code here.....

The idea here is: If the user has closed the form (it's disposed), then
just ignore the incoming data. If the form is still open, show the data via
some painting code, but do so on the form's UI thread. Nothing too unusual
here. My question is: What happens if BeginInvoke() is called on a form that
is already disposed? This could happen in the following situation:

You will get a runtime exception, stating that Invoke is called on an
already disposed object.

But the chance is small.
In the code above, let's say that the this.BeingInvoke() call is just
about to execute, but then a context switch happens and the user's request
to close the window transpires which leads to a Dispose() call on the form.
The context then switches back to my BeginInvoke() call which would then be
made against a disposed form. Is this a problem? From my perspective, I [...]

This could lead to problems. I had this problem myself in a similar
setting.

Only, I forgot to check IsDisposed :-S. If you are really frantic about the
small chance that the form will already be closed, guard it like I did. If
you aren't then follow Nicholas' advice :-D.
 
D

David Sworder

message > >You will get a runtime exception, stating that Invoke is called
on an
already disposed object.

bummer... That's what I was afraid of.
By the way, I would do the painting in OnPaint, and just call Ivalidate if
the data arrives (which your callback signals). Invalidate triggers the
painting.

good idea.
This could lead to problems. I had this problem myself in a similar setting.
I solved it by keeping a flag to indicate whether the form is closing.
Initialize the flag to false.Make a lock object for the closing flag. In the
OnClosing handler, begin with this:

lock( closingLock )
{
isClosing = true;
}
// ... other closing code
[...]

Excellent advice... a couple of follow ups:

Is 'closingLock' a bool value type? I don't think I'm allowed to obtain
a lock on a value type (am I?). So it sounds like the two places that I'll
want to use the lock are in the Dispose() override and in the callback...
but still, even with the lock, couldn't I end up with a situation where
BeginInvoke() is called successfully, the lock is released, Dispose() is
then called and the lock is released, then the runtime attempts to call the
delegate passed to BeginInvoke()? In that case, if the delegate is called,
my callback function would simply check 'IsDisposed' and return
immediately... but I'm wondering if perhaps .NET will have "issues" with
calling a delegate on a Form that is disposed?
 
N

Nicholas Paldino [.NET/C# MVP]

Tom and David,

If the form was the last one, then it will try and use the handle on
that form. As mentioned before, if it is Disposed, then an exception should
be thrown. However, when the form is an MDI child form, or when dealing
with a control, the window handle that the message is sent on is not always
the handle that is associated with the control.

Which control is dependent on the parent of the Form/Control.

The reason for this behavior is that the Form class's implementation of
Invoke/BeginInvoke is inherited from Control.
 
T

TT \(Tom Tempelaere\)

David Sworder said:
message > >You will get a runtime exception, stating that Invoke is called
on an
already disposed object.

bummer... That's what I was afraid of.
By the way, I would do the painting in OnPaint, and just call Ivalidate if
the data arrives (which your callback signals). Invalidate triggers the
painting.

good idea.
This could lead to problems. I had this problem myself in a similar setting.
I solved it by keeping a flag to indicate whether the form is closing.
Initialize the flag to false.Make a lock object for the closing flag. In the
OnClosing handler, begin with this:

lock( closingLock )
{
isClosing = true;
}
// ... other closing code
[...]

Excellent advice... a couple of follow ups:

Is 'closingLock' a bool value type? I don't think I'm allowed to
obtain

No.

private readonly object closingLock = new object();
a lock on a value type (am I?). So it sounds like the two places that I'll

No I didn't think so.
want to use the lock are in the Dispose() override and in the callback...

Mmm. In dispose override? Why not in the closing handler of the form (or
control)?
but still, even with the lock, couldn't I end up with a situation where
BeginInvoke() is called successfully, the lock is released, Dispose() is
then called and the lock is released, then the runtime attempts to call the
delegate passed to BeginInvoke()? In that case, if the delegate is called,

No. The callback is merely registered for execution, and can only execute if
the message loop for the form is active. When it is disposed, I don't think
that the form still has a message loop, so the delegate cannot execute (and
cannot be executing because the execution of the delegate is synchronized
with the thread of the UI component).

But if you lock from the onclosing handler, you are certain that when
BeginInvoke is executing in the callback, that the form still has a message
loop.
 
J

Justin Rogers

http://weblogs.asp.net/justin_rogers/articles/126345.aspx

The full pattern and logic behind the UI thread marshalling behavior is
explained
in the above link. Might be additionally helpful to those that want the
gruesome
details of exactly how and where the marshaling control is retreived from.


--
Justin Rogers
DigiTec Web Consultants, LLC.
Blog: http://weblogs.asp.net/justin_rogers

Nicholas Paldino said:
Tom and David,

If the form was the last one, then it will try and use the handle on
that form. As mentioned before, if it is Disposed, then an exception should
be thrown. However, when the form is an MDI child form, or when dealing
with a control, the window handle that the message is sent on is not always
the handle that is associated with the control.

Which control is dependent on the parent of the Form/Control.

The reason for this behavior is that the Form class's implementation of
Invoke/BeginInvoke is inherited from Control.
 

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