TiP: Beware casting Window(i)

Sometimes you need to have each of your windows of a certain type update or do something. It’s tempting to right code like this

For i As Integer = 0 To WindowCount - 1
		If Window(i) <> nil then
			If Window(i) isa MyWindow then
				MyWindow(Window(i)).DoSomething
			End If
		End If
Next

But I’ve found that rarely between the check that the window(i) is MyWindow and the casting of the window(i) to MyWindow, the window order changes in the background resulting in a bad casting.

You can either put all the windows in an array first and work on that or use something like this

dim w as Window
For i As Integer = 0 To WindowCount - 1
w = Window(i)
		If w <> nil then
			If w isa MyWindow then
				MyWindow(w).DoSomething
			End If
		End If
Next

I assume that the second bit would work as w now a reference to a specific window and not a reference to whatever window happens to exist at any moment at Window(i). Right?

That would be a very rare occurrence!
But in that case, depending on how the order changes. your code does not guarantee you would actually iterate through all windows.

Of that is a real concern, this would work better I think:

[code]Dim MyWinArr() as MyWindow, W as Window

#pragma BackgroundTasks False

For i As Integer = WindowCount - 1 DownTo 0
W = Window(i)
If W <> Nil AND W IsA MyWindow then MyWinArr.Append MyWindow(W)
Next

#pragma BackgroundTasks True

For Each MyWin as MyWindow in MyWinArr
MyWin.DoSomething
Next
[/code]

I thought it would be really rare too but it actually happens a few times a day with occasional usage.

I too worry that you might not catch all windows by looping through and grabbing them. However I also suspect that stopping background task won’t help because the windows are being moved around the OS, not Xojo. I could be wrong.

Been testing on OS X.

Interesting. I’ve never had this problem and I do this exact sequence on a regular basis.

It took me years before I learned what was happening. Users would choose a status bar menu (with the actual app in the background) and the method would rarely fail due to illegal casting exception.

Maybe it happens more often when the app is not frontmost. I’ve seen it while debugging too but that often entails the app in the background.

We’ve seen this in the IDE itself :slight_smile:
It definitely can happen

So, if I loop through Window() grabbing them, is there a chance I’ll miss a window because the window order changed mid loop?

Should I loop through 2-3 times and compare to ensure I got everything?

I’ve never seen one get missed
I usually do the loop from WindowCount() - 1 downto 0 as any window that becomes frontmost will move towards index 0 rather than others being moved to higher indexes

That makes sense. Good tip.

This kind of thing is why I’d recommend decoupling and using an event bus so that all of those MyWindows that would be interested in the reason for running DoSomething register themselves and their (weak) reference is therefore in a stable array.

My little EventBusIJ library has served me well for this.

https://www.ianmjones.com/projects/xojo/eventbusij/

It is usually good practice in programming to take out the Window into variable. Since Window(i) runs function of undefined magnitude, thus you would be calling it 3 times instead of just once.

And of course what you describe that it can and will sometimes change.

This does not only go for Window of course but many other things that people often take the easy way and just call many times without thinking what kind of code might be executed behind it.

[quote=319817:@Stephen Dodd] too worry that you might not catch all windows by looping through and grabbing them. However I also suspect that stopping background task won’t help because the windows are being moved around the OS, not Xojo. I could be wrong.

[/quote]

I think with a with a single processor/core system my code would guarantee the windows would not reorder, while you are collecting them. Not sure in the multicore world we now live in… BUT my code would collect the window very quickly and so minimize the chance of missing one… (but of course a window could close when processing the array so another nil check would be required then …

That said, IMO, the solution solution mentioned here here would be the registration suggestion, along with a check for existence right before DoSomething is called.

  • Karen

I would suggest you watch http://developer.xojo.com/webinar-design-patterns in particular the observer pattern.

[quote=319881:@Karen Atkocius]I think with a with a single processor/core system my code would guarantee the windows would not reorder, while you are collecting them. Not sure in the multicore world we now live in… BUT my code would collect the window very quickly and so minimize the chance of missing one… (but of course a window could close when processing the array so another nil check would be required then …

That said, IMO, the solution solution mentioned here here would be the registration suggestion, along with a check for existence right before DoSomething is called.

  • Karen[/quote]

It actually has little to do with multi core vs single core
More to do with what DoSomething might do as it could run code which causes the ordering of windows to change or even cause a window to close again thereby causing the order to change
Collecting all the windows into an array before calling the method means that this wont affect you and it will temporarily hold whatever window might close open since there is a reference to it

[quote=319891:@Norman Palardy]It actually has little to do with multi core vs single core
More to do with what DoSomething might do as it could run code which causes the ordering of windows to change or even cause a window to close again thereby causing the order to change
[/quote]

In my understanding of the BackgroundTasks pragma, in a single core system the loop in my code above, which collects the windows to be processed later would not yield to anything, so would have a list of all the windows open at the time that loop ran. In a multi-core system the UI of other apps could still be active so window order could change during that first loop.

Is that right?

In any case, In the code I posted DoSomething is run outside that loop and outside the scope of that pragma… so at that time if the windows change order it has no effect on collecting them during the first loop… Though of course one may have been closed or a new one created by then.

  • Karen

collecting them then running the method is definitely safer
I was just trying to explain why - and the multi core has nothing to do with it
nor does background tasks for the most part

IF a side effect of “do something” is to close a window (or worse several) then that can affect whether the loop works as expected or not