I have a Control object that has as a property a code-instantiated free-running timer. The (WeakAddress) delegate for the timer’s action event raises an event which is handled by the object’s parent window . Sometimes when the app quits I get a NOE, presumably because the window is gone but the object still exists and the timer is still firing. What’s the best practice for handling such cases to ensure that timers aren’t calling code that’s been destroyed? Is it as simple as using AddressOf instead of WeakAddressOf, or is there more to it?
Tear down the Timer in the Window’s Close or CancelClose event.
That’s why I never use CallLater. If you have the Timer as a property, set it’s RunMode to ‘Off’ and the property to ‘nil’ in the Window’s Close event.
Edit: Not to forget to ‘RemoveHandler’.
What’s the problem with
Timer.CancelCallLater(AddressOf method)
in the window’s close event?
P.S. Actually you don’t even need that, the window and app close just fine (at least on my Mac here) with the timer still waiting to call …
It’s not a CallLater, it’s a timer that sits there firing continuously, i.e. “free-running”.
I haven’t seen the problem on Mac, only under Windows.
That sounds like it would work, but it leads to questions about what else I need to handle myself on app quit, and why doesn’t the framework handle this for you, i.e. shouldn’t it first destroy everything that can generate events at random, like timers and sockets, before destroying their parents? And if this is something the user does need to do, why is it not mentioned in the documentation?
Also, how do you tear down a timer? Set it = Nil?
Set its RunMode to off. It’s unclear to me why doing anything else would be required.
I’m not convinced that this is really the source of your trouble, but since every call to AddHandler
should have a corresponding call to RemoveHandler
, this is how I typically handle it:
if myTimer isa object then
myTimer.RunMode = Timer.RunModes.Off
RemoveHandler myTimer.Action, WeakAddressOf TimerMethod
myTimer = nil
end if
I usually check if the controls of the window still exist in such cases. Random code example from the result of a thread:
if bbCheckImap = nil then Return ' when closing window
#Pragma BreakOnExceptions False
try
bbCheckImap.Caption = kCheck
'other code
catch err as NilObjectException
'ignore, window is closing
end try
#Pragma BreakOnExceptions True
A bb control is always a BevelButton.
That I’m wrong?
Let’s see if this code change fixes it.
have your Control object a Destructor Method where you can clean up something?
Do you use DoEvents anywhere?
Nice try, but no, lol!
I assume the window is closing as the app is shut down, so do you have a long process/loop/thread running during the close of the window?
No threads or “long” processes per se, but there are a few (half a dozen at most, I think) timers going, and some continuous incoming serial data bursts at 100ms intervals, each of which involves some handling.
The fault is not consistently reproducible, but Murphy will see to it that as soon as the client tests it, it will happen for them
Do you use any declares?
32 or 64 bit exe?
32-bit build. There are some declares that extract USB device info, which only get called when SerialDevice.Count changes, which should not happen as a result of the app quitting.
Which Xojo version?
2021 r2.1, under Mac Mojave.