How can i know if it is safe to call Invoke on a control?

M

Mehdi

Hi,

If you want to do some operations on a UI control from a thread other than
the UI thread, you've got to first marshall the call to the UI thread using
Control.Invoke or Control.BeginInvoke. So i've created in my application a
UI control solely for the purpose of marchalling operations to the UI
thread and i'm using code like that to do the marshalling:

private void NotifyStatusChanged(Status newStatus)
{
if (m_uiControl.InvokeRequired)
{
// Marshall call to UI thread
m_uiControl.BeginInvoke(new
NotifyStatusChangedDelegate(NotifyStatusChanged), new object[]
{newStatus});
}
else
{
// We are in the UI thread now
// Do whatever we need to do
}
}

This works fine while the application is running.

When i close the application, the above method is called one last time
before everything stops and that's where it blows up: an
InvalidOperationException "Cannot call Invoke or InvokeAsync on a control
until the window handle has been created" is thrown when BeginInvoke is
called. I know that the control's handle has been created since i've
created it myself and it worked fine for the whole duration of the
application anyway. The IsHandleCreated property of my control returns true
when the exception is thrown. Its IsDisposed and Disposing properties both
return false.

So does anybody know why i get this exception and how i could check before
calling Invoke or BeginInvoke that my control is in a state suitable for
this operation? I could of course wrap the call around a try/catch block
and swallow the exception but I'd prefer to understand what the real
problem is.

Thanks.
 
H

Herfried K. Wagner [MVP]

Mehdi said:
If you want to do some operations on a UI control from a thread other than
the UI thread, you've got to first marshall the call to the UI thread
using
Control.Invoke or Control.BeginInvoke. So i've created in my application a
UI control solely for the purpose of marchalling operations to the UI
thread and i'm using code like that to do the marshalling:

Which value does the control's 'Handle' property return?
 
G

Guest

When i close the application, the above method is called one last time
before everything stops and that's where it blows up: an
InvalidOperationException "Cannot call Invoke or InvokeAsync on a control
until the window handle has been created" is thrown when BeginInvoke is
called.
...
I could of course wrap the call around a try/catch block
and swallow the exception but I'd prefer to understand what the real
problem is.

I've seen similar shutdown issues in my multithread programs. For my own
sanity, I go to the following extremes. I have a global boolean
AppIsShuttingDown flag initialized to false. When I shutdown, I set it to
true. In form_closing, I also set it to true in case I am being shut down
for a reason my program can't know. I use a try/catch block for invoke, and
if an exception is thrown, I test my boolean. If I am shutting down, I
ignore the exception, and if I am not shutting down, I report to myself (eg
via an assertion if running in the ide). FYI assert failures are rare and
unusual, eg when shutting down my app from the task manager.

Why should I have to do this? I don't know, and like you, I don't like not
knowing. But clearly, once shutdown has progressed enough, some
functionality is broken that is exposed only in a multithread environment.
My response to this sate of affairs is to continue not knowing but use the
above workaround. I suggest you do the same because a complete answer will
never come imo.
 
S

Stoitcho Goutsev \(100\)

Mehdi,

This question was asked yesterday or the day before. My suggestion was to
check control's IsDisposed and Disposing properties and not to call Invoke
method if either of them return true. It is probably good idea to check
HandleCreated property as well.

However someone else in the group suggested that The method can enter the
disposing state right after Invoke is called and before the call to be
dispatched. In this case you may get again the exception. The solution for
that would be to put your Invoke call in try/catch block.
I never tried but according to their posts the exception is correctly
marshaled back to the calling thread.
 
M

Mehdi

Which value does the control's 'Handle' property return?

Thanks for your question, trying to answer it made me discover something.
At the start of my program, i'm creating my marshalling UI control and
accessing its Handle property to force its creation (since i've learned the
hard way that Handles are lazy-initialized). So everything works fine until
i try to close my application.

Until now, when the InvalidOperationException was thrown, i used to break
into the visual studio debugger to try to find out what was wrong and the
first thing i did was to type "? m_uiControl" in the Command window to see
how my control looked like. Then i would type "?
m_uiControl.IsHandleCreated" or "? m_uiControl.Handle" to examine these
properties. But typing "? m_uiControl" in the Command window actually
causes Visual Studio to access every property of the control, including the
Handle property, and therefore forces the Handle to be recreated (because
at this state it had effectively been destoyed). This is why the subsequent
calls to "? m_uiControl.IsHandleCreated" returned true since Visual Studio
had recreated a new Handle for me! If i type "?
m_uiControl.IsHandleCreated" first thing after i break into the debugger,
it returns 'false'.

So, to sum up, the exception is thrown because the handle of my marshalling
control has been destoyed (which is to be expected since the program is
closing). I now know why i'm getting this exception.

The next problem is to find out how to avoid this problem. The
documentation says that only BeginInvoke, EndInvoke, Invoke, InvokeRequired
and CreateGraphics are safe for multithreaded operations. IsHandleCreated
is not part of this list. So what's the proper way to check wether the
handle is still there before calling BeginInvoke or Invoke?
 
H

Herfried K. Wagner [MVP]

Mehdi said:
documentation says that only BeginInvoke, EndInvoke, Invoke,
InvokeRequired
and CreateGraphics are safe for multithreaded operations. IsHandleCreated
is not part of this list. So what's the proper way to check wether the
handle is still there before calling BeginInvoke or Invoke?

I fear you'll have to use 'Control.Invoke' or similar to get the value of
the 'IsHandleCreated' property from another thread.
 
M

Mehdi

Hi Stoitcho,

This question was asked yesterday or the day before. My suggestion was to
check control's IsDisposed and Disposing properties and not to call Invoke
method if either of them return true. It is probably good idea to check
HandleCreated property as well.

Thanks for the tip, I've had a look at this dicussion thread. Checking the
IsDisposed and Disposing properties before calling Invoke does not work:
they are always false in my case. I could check the IsHandleProperty
though, this seems to be more reliable. However, the documentation clearly
states that this property is not thread safe and as said in the other
thread, the handle might be destroyed after you've checked wether it's been
created but before Invoke has completed, so it is not 100% reliable.

From all the info that i've been able to gather so far, it seems that there
is no reliable way to check beforehand wether a call to Invoke or
BeginInvoke is safe and that any call to either of these methods should be
placed in a try/catch block. Unless i've missed something somewhere, this
fact and the complete lack of documentation about this problem seems to me
to be a quite serious bug.
 
M

Mehdi

From all the info that i've been able to gather so far, it seems that there
is no reliable way to check beforehand wether a call to Invoke or
BeginInvoke is safe and that any call to either of these methods should be
placed in a try/catch block.

In addition to what i've just said: the exception that you need to catch
whenever you're calling Control.Invoke() or Control.BeginInvoke is
InvalidOperationException (this is the exception thrown whenever Invoke or
BeginInvoke is called on a Control that has had its Handle destroyed). This
will take care of catching the ObjectDisposedException as well which might
be thrown if your Control has already been disposed by the time you're
trying to Invoke.
 
R

RichS

Not sure if this is of any use, but I have had big problems in this
area before. The problem that I had ( which doesnt sound exactly like
your problem, but may be related ) was due to the visibility status of
a control.

If you set a control Visible to false *before* you add it to a control
then the handle is not created. The problem with this is that when you
call "control.InvokeRequired" for that control, it is that thread that
will then create the Handle. If this is a multi-threaded app and you
want to be correct and test all controls for InvokeRequired then it
will completely screw up the whole app.

There are 2 cheats that seem to help:
* Straight after creating the controls, add "IntPtr intPtr =
control.Handle;", which forces the creation
* DO NOT set the visibility status before adding the control to a
control.

RichS
 
M

Mehdi

If you set a control Visible to false *before* you add it to a control
then the handle is not created. The problem with this is that when you
call "control.InvokeRequired" for that control, it is that thread that
will then create the Handle. If this is a multi-threaded app and you
want to be correct and test all controls for InvokeRequired then it
will completely screw up the whole app.

Yes i've had the same problem before and it took me hours to find out why
my app kept crashing randomly even though i had taken care of marshalling
all the calls to UI controls to the UI thread. I eventually discovered what
you are describing here and this is why i now always create a Control as
soon as my application starts that will be used only for calls marshalling.
I make sure that its handle is valid by accessing its Handle property just
after i've created it as you described.

My problem here however is that even if your control has a valid handle for
the whole duration of your program, its handle will be automatically
destroyed at some point in the shutdown process of your application which
will cause any subsequent calls to Invoke or BeginInvoke to fail. And there
seems to be no thread safe way to detect when this happens or to check if
that has already happened before calling Invoke or BeginInvoke.
 

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