How to safely Form.Invoke from another thread? [modified]
-
Hello, how do I safely invoke a delegate on my windows form if I can't eliminate the possibility that the form gets disposed BETWEEN me checking whether it is disposed and the invoke code itself? Randomly I'm getting an exception that says I can't access the disposed object. It's absolutely clear that I can't access a disposed object, but how do I make sure it's indeed NOT disposed if it can still get disposed right after I check whether it is disposed? :) Consider this example that looks correct, but produces the above mentioned exception from time to time:
if (!MyForm.IsDisposed) { // what if the form gets disposed BETWEEN the above line // of code and the following line of code? MyForm.Invoke(MyDelegate); }
Form.Disposing doesn't help neither. Thanks very much for any input, Michal -- modified at 7:24 Wednesday 25th July, 2007
-
Hello, how do I safely invoke a delegate on my windows form if I can't eliminate the possibility that the form gets disposed BETWEEN me checking whether it is disposed and the invoke code itself? Randomly I'm getting an exception that says I can't access the disposed object. It's absolutely clear that I can't access a disposed object, but how do I make sure it's indeed NOT disposed if it can still get disposed right after I check whether it is disposed? :) Consider this example that looks correct, but produces the above mentioned exception from time to time:
if (!MyForm.IsDisposed) { // what if the form gets disposed BETWEEN the above line // of code and the following line of code? MyForm.Invoke(MyDelegate); }
Form.Disposing doesn't help neither. Thanks very much for any input, Michal -- modified at 7:24 Wednesday 25th July, 2007
It helps to set a flag that the form is closing and check for that flag before the invoke, but it's not an elegant solution and it might still throw an exception if the flag is set after checking for the flag and the form gets disposed before executing the invoke. Any ideas? Thanks, Michal
-
It helps to set a flag that the form is closing and check for that flag before the invoke, but it's not an elegant solution and it might still throw an exception if the flag is set after checking for the flag and the form gets disposed before executing the invoke. Any ideas? Thanks, Michal
How about wrapping both your
protected override void Dispose(bool disposing)
-method and your invoke-procedure in alock(...)
-block? Ref.: http://msdn2.microsoft.com/en-us/library/c5kehkcz(vs.80).aspx -
How about wrapping both your
protected override void Dispose(bool disposing)
-method and your invoke-procedure in alock(...)
-block? Ref.: http://msdn2.microsoft.com/en-us/library/c5kehkcz(vs.80).aspxCool, thanks a lot! This solutions seems to work. Next time, I'll be wiser and will use lock for this kind of situation. Thanks much again!! Michal
-
How about wrapping both your
protected override void Dispose(bool disposing)
-method and your invoke-procedure in alock(...)
-block? Ref.: http://msdn2.microsoft.com/en-us/library/c5kehkcz(vs.80).aspxare you kidding ? the Dispose() method is the one that typically will be executed by the finalizer thread. Do you think it will appreciate your lock ? if the finalizer cant do its job, the GC has less memory to manage and recycle; do you want the GC to run out of memory for no good reason ? I am not sure what the right answer is, but what you propose seems very wrong to me. :confused:
Luc Pattyn
try { [Search CP Articles] [Search CP Forums] [Forum Guidelines] [My Articles] } catch { [Google] }
-
are you kidding ? the Dispose() method is the one that typically will be executed by the finalizer thread. Do you think it will appreciate your lock ? if the finalizer cant do its job, the GC has less memory to manage and recycle; do you want the GC to run out of memory for no good reason ? I am not sure what the right answer is, but what you propose seems very wrong to me. :confused:
Luc Pattyn
try { [Search CP Articles] [Search CP Forums] [Forum Guidelines] [My Articles] } catch { [Google] }
Actually, it does not appreciate my lock :) If I'm forcing the Dispose() to wait on the lock until all Invokes are done and until I make sure there are no Invokes going to be done in the future, I'm effectively blocking that form's Window Message Queue. If I'm blocking the Window Message Queue, the Invoke never returns. Obviously, the same happens (WMQ block => Invoke never returns) if I try, for instance, to block after Form.Closing event etc. So I'm back at the beginning :) Any suggestions? :) Thanks, Michal
-
Actually, it does not appreciate my lock :) If I'm forcing the Dispose() to wait on the lock until all Invokes are done and until I make sure there are no Invokes going to be done in the future, I'm effectively blocking that form's Window Message Queue. If I'm blocking the Window Message Queue, the Invoke never returns. Obviously, the same happens (WMQ block => Invoke never returns) if I try, for instance, to block after Form.Closing event etc. So I'm back at the beginning :) Any suggestions? :) Thanks, Michal
I'm sure I would have some suggestions (or at least comments) if I felt I understood your problem. You seem to somehow Dispose() of a form, while you may still need it. So I need more info (contextual I guess). For what it's worth: I typically dont dispose of forms, I just close them. I realize you may want to avoid Close() so you can play with Hide() and Show() again, but even then the last time you could Close() rather than Dispose(), couldnt you ? :)
Luc Pattyn
try { [Search CP Articles] [Search CP Forums] [Forum Guidelines] [My Articles] } catch { [Google] }
-
I'm sure I would have some suggestions (or at least comments) if I felt I understood your problem. You seem to somehow Dispose() of a form, while you may still need it. So I need more info (contextual I guess). For what it's worth: I typically dont dispose of forms, I just close them. I realize you may want to avoid Close() so you can play with Hide() and Show() again, but even then the last time you could Close() rather than Dispose(), couldnt you ? :)
Luc Pattyn
try { [Search CP Articles] [Search CP Forums] [Forum Guidelines] [My Articles] } catch { [Google] }
Hi, Luc, thanks for your comment. I'm not disposing of the form myself. I'm just closing it. But the whole point is in not allowing the form to get disposed before all possible Invokes on it are done. I can briefly outline what is going on in my app: a server application is calling my client app every 100 ms via COM and refreshes its state. Sometimes it happens that the server stops calling my client app and I need to detect that. For that purpose I've written a simple timer class that's started together with the app. The core class of my app stores the timestamp of the last COM call and my timer class checks every 200 ms whether the difference between DateTime.Now() and last COM call timestamp is higher than maximum allowed. If it is, it Invokes a method on a form that's telling me the connection has been lost. If it's not, it Invokes a method on a form that's telling me the connection is OK. Obviously, the timer runs on a separate thread from the form's thread. When I'm closing the form (which does not close the application itself), I need to make sure that the timer executes the last Invoke on the form and shuts itself off. And there's the problem. In order to make sure that the timer executes the last Invoke on the form safely, I need to prevent the form from disposing of itself before that last Invoke is done. But if I'm preventing the form from disposing of itself by putting a lock anywhere between the moment when the form starts closing and the moment when it starts getting disposed of, I'm effectively blocking the window message queue, so the last Invoke (which is executed while the form waits on a lock) never returns. Thanks much for any help. Michal
-
Hi, Luc, thanks for your comment. I'm not disposing of the form myself. I'm just closing it. But the whole point is in not allowing the form to get disposed before all possible Invokes on it are done. I can briefly outline what is going on in my app: a server application is calling my client app every 100 ms via COM and refreshes its state. Sometimes it happens that the server stops calling my client app and I need to detect that. For that purpose I've written a simple timer class that's started together with the app. The core class of my app stores the timestamp of the last COM call and my timer class checks every 200 ms whether the difference between DateTime.Now() and last COM call timestamp is higher than maximum allowed. If it is, it Invokes a method on a form that's telling me the connection has been lost. If it's not, it Invokes a method on a form that's telling me the connection is OK. Obviously, the timer runs on a separate thread from the form's thread. When I'm closing the form (which does not close the application itself), I need to make sure that the timer executes the last Invoke on the form and shuts itself off. And there's the problem. In order to make sure that the timer executes the last Invoke on the form safely, I need to prevent the form from disposing of itself before that last Invoke is done. But if I'm preventing the form from disposing of itself by putting a lock anywhere between the moment when the form starts closing and the moment when it starts getting disposed of, I'm effectively blocking the window message queue, so the last Invoke (which is executed while the form waits on a lock) never returns. Thanks much for any help. Michal
OK, so I resolved the issue. It's certainly not the most elegant solution, but it works. I will still appreciate if someone tells me how this should be done "professionally" :) Instead of a lock, I'm using an ordinary while loop. The while loop has an advantage over lock of being able to execute something while it's waiting for a signal. Lock simply sits there and does nothing until the lock is released. So I placed the while loop inside the Form.Closing() event handler. This while loop is blocking the form from closing itself (and thus disposing of itself). Inside this loop (apart from waiting for a signal) I'm executing Application.DoEvents() every 50 ms. This ensures that the Invoke, waiting on the second thread, gets processed. If you've got some better suggestion, please tell me. Thanks, Michal