PC Review
Forums
Newsgroups
Microsoft DotNet
Microsoft Dot NET Framework Forms
How can i know if it is safe to call Invoke on a control?
Forums
Newsgroups
Microsoft DotNet
Microsoft Dot NET Framework Forms
How can i know if it is safe to call Invoke on a control?
![]() |
How can i know if it is safe to call Invoke on a control? |
|
|
Thread Tools | Rate Thread |
|
|
#1 |
|
Guest
Posts: n/a
|
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. |
|
|
|
#2 |
|
Guest
Posts: n/a
|
"Mehdi" <vioccc@REMOVEME.gmail.com> schrieb:
> 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? -- M S Herfried K. Wagner M V P <URL:http://dotnet.mvps.org/> V B <URL:http://classicvb.org/petition/> |
|
|
|
#3 |
|
Guest
Posts: n/a
|
> 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 Ap****huttingDown 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. |
|
|
|
#4 |
|
Guest
Posts: n/a
|
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. -- Stoitcho Goutsev (100) "Mehdi" <vioccc@REMOVEME.gmail.com> wrote in message news:95p7vyzyxfn5.13bqdnm6epali$.dlg@40tude.net... > 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. |
|
|
|
#5 |
|
Guest
Posts: n/a
|
On Thu, 9 Feb 2006 14:42:10 +0100, Herfried K. Wagner [MVP] wrote:
> "Mehdi" <vioccc@REMOVEME.gmail.com> schrieb: >> 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? 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? |
|
|
|
#6 |
|
Guest
Posts: n/a
|
"Mehdi" <vioccc@REMOVEME.gmail.com> schrieb:
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? I fear you'll have to use 'Control.Invoke' or similar to get the value of the 'IsHandleCreated' property from another thread. -- M S Herfried K. Wagner M V P <URL:http://dotnet.mvps.org/> V B <URL:http://classicvb.org/petition/> |
|
|
|
#7 |
|
Guest
Posts: n/a
|
Hi Stoitcho,
On Thu, 9 Feb 2006 09:55:49 -0500, Stoitcho Goutsev (100) wrote: > 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. |
|
|
|
#8 |
|
Guest
Posts: n/a
|
On Thu, 9 Feb 2006 16:31:17 +0100, Herfried K. Wagner [MVP] wrote:
> "Mehdi" <vioccc@REMOVEME.gmail.com> schrieb: > 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? > > I fear you'll have to use 'Control.Invoke' or similar to get the value of > the 'IsHandleCreated' property from another thread. :-) |
|
|
|
#9 |
|
Guest
Posts: n/a
|
On Thu, 9 Feb 2006 15:47:33 +0000, Mehdi wrote:
> 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. |
|
|
|
#10 |
|
Guest
Posts: n/a
|
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 |
|
![]() |
|
| Thread Tools | |
| Rate This Thread | |
|
|

Main Page 

