Control.Invoke() blocking forever if form closed whilst background task running

D

Dave Parker

In part of the system I am working on I have a background task (started with
a call to BeginInvoke() on a delegate), which at some point needs to read a
property from one of the controls on a form.

Normally this all works great. The problem I am having is if the user closes
the form whilst the background task is running. Execution blocks
indefinitely on the call to Control.Invoke() and the method I supply to it
never begins. The application's message queue doesn't seem busy and the UI
is still responsive other than I have this thread sitting in the background,
stuck on this call. I am calling Invoke() on a custom UserControl that has
not yet been disposed.

The stack trace to where it is blocking inside Invoke() shows up as the
following...

[In a sleep, wait, or join]
mscorlib.dll!System.Threading.WaitHandle.WaitOne(long timeout, bool
exitContext) + 0x2e bytes
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout,
bool exitContext) + 0x23 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle
waitHandle = {System.Threading.ManualResetEvent}) + 0xa1 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control
caller, System.Delegate method, object[] args, bool synchronous) + 0x36d
bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.Invoke(System.Delegate
method, object[] args) + 0x48 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.Invoke(System.Delegate
method) + 0x7 bytes


I can get around the problem by replacing the Invoke() call with a
BeginInvoke() and therefore being able to specify a timeout, but I'm curious
as to why it's happening in the first place.

Thanks
Dave
 
P

Peter Duniho

[...]
Normally this all works great. The problem I am having is if the user
closes
the form whilst the background task is running. Execution blocks
indefinitely on the call to Control.Invoke() and the method I supply to
it
never begins.

If the control is no longer part of the active UI, then I would not expect
the fact that it's not yet disposed to be of much importantance. It is
still not surprising that a control no longer part of the active UI does
not receive any more window messages, which are needed for the Invoke() to
succeed.

The solution is obviously to not try to interact with the control once
it's no longer part of the active UI. How best to do this will depend on
your own code, but it seems you are knee-deep in synchronization issues
anyway, so it should not be hard to figure out.

Pete
 
D

Dave Parker

Thanks, this is what I ended up with...

TimeSpan tsTimeout = TimeSpan.FromSeconds(45);
TimeSpan tsPollFreq = TimeSpan.FromMilliseconds(200);

DateTime dtTimeoutTime = DateTime.Now.Add(tsTimeout);

IAsyncResult asyncResult = ui.BeginInvoke(method);

while (!asyncResult.IsCompleted)

{

asyncResult.AsyncWaitHandle.WaitOne(tsPollFreq, false);

if(ui.IsDisposed || ui.Disposing)

throw new ObjectDisposedException("The invocation target has been disposed
or is in the process of being disposed.");

if(DateTime.Now>dtTimeoutTime)

throw new TimeoutException("An invocation on the user interface timed out
after 45 seconds.");

}



Peter Duniho said:
[...]
Normally this all works great. The problem I am having is if the user
closes
the form whilst the background task is running. Execution blocks
indefinitely on the call to Control.Invoke() and the method I supply to
it
never begins.

If the control is no longer part of the active UI, then I would not expect
the fact that it's not yet disposed to be of much importantance. It is
still not surprising that a control no longer part of the active UI does
not receive any more window messages, which are needed for the Invoke() to
succeed.

The solution is obviously to not try to interact with the control once
it's no longer part of the active UI. How best to do this will depend on
your own code, but it seems you are knee-deep in synchronization issues
anyway, so it should not be hard to figure out.

Pete
 

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