InvokeRequired incorrect on Form close/dispose

D

david.dearing

I have a Form that consists of multiple views with corresponding
controllers (MVC). The presenters are listening to events that may
come from non-UI threads. I am currently using InvokeRequired and
BeginInvoke to handle the events appropriately.

generic code from controller.cs:

public void OnChange(EventArgs args)
{
if (_view.InvokeRequired)
{
_view.BeginInvoke(new OnChange(OnChange), new object []
{args});
}
else
{
// ... handle event args
}
}

When I close the main Form (via the "x" in the corner), the controller
is still receiving events (from a Service that doesn't close). I have
found that once the Form begins to close, InvokeRequried doesn't return
the correct result and the application blocks. (More info here:
http://pdxjjb.blogspot.com/2003_08_01_pdxjjb_archive.html#106047473215550135).

To remedy this, I have tried adding logic to determine if the view or
any of its Parent controls (the view is nested within the Form) are
IsDispose-ing or Dispose-d. This works for several calls (which I've
seen via logging) but will still block in some cases. The code I added
is:

private bool CheckViewDisposal(Control c)
{
if (c == null) return false;

return c.IsDisposed || c.Disposing || Disposing(c.Parent);
}


public void OnChange(EventArgs args)
{
if (CheckViewDisposal(_view)) return;

if (_view.InvokeRequired)
{
...

What am I missing? Why does the view return false for InvokeRequired
as well as its Disposal state when the handler is called from a non-UI
thread?

Any thoughts? Thanks!
 
M

Mehdi

private bool CheckViewDisposal(Control c)
{
if (c == null) return false;

return c.IsDisposed || c.Disposing || Disposing(c.Parent);
}


public void OnChange(EventArgs args)
{
if (CheckViewDisposal(_view)) return;

if (_view.InvokeRequired)
{
...

What am I missing? Why does the view return false for InvokeRequired
as well as its Disposal state when the handler is called from a non-UI
thread?

Because your OnChange method is called from a worker thread while the Form
is disposed in the UI thread. It is therefore perfectly possible that the
form has not yet been disposed when CheckViewDisposal is called but is
disposed in between CheckViewDisposal and InvokeRequired, hence the result.
There is unfortunaly no way I know of to get rid of this problem. What you
could do is create a dummy control in a class that is not a form and will
not get disposed until the very end (your class doing the async stuff for
example), force the creation of its handle by accessing it then use this
dummy control for all your InvokeRequired, Invoke() and BeginInvoke()
calls:

Control marshallingControl = new Control();
IntPtr handle = marshallingControl.Handle;

Of course, you have to be careful to create this dummy control in the UI
thread. Also, you should always enclose your Invoke() and BeginInvoke() in
a try/catch block, catching InvalidOperationException since this exception
will be raised if you try to invoke on a control that has been disposed.

Since you seem to be interested in how InvokeRequired works, have a look at
this article: <http://weblogs.asp.net/justin_rogers/articles/126345.aspx>
 

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