I have macOS desktop app that contains a method that is used to count the number of WAV files in a folder, and calls itself when it finds a sub folder. I have a client using my app who has reported an error while loading a folder tree to scan all WAVs on his system. He has sent the following stack from my app’s un-handled exception window:
Does “RaiseExceptionClassRK13ClassDeclBase” mean anything to anyone, or does the users folder structure simply contain too many sub-folders for the stack to handle? I’ve tried to re-produce this on macOS 10.15.7. I managed to make 86 sub-folders, the maximum Finder seems to handle before it starts throwing errors. If I load 4x WAVs into the last folder my app can scan this tree okay.
Would the following code catch the above error:
Exception err
If err IsA StackOverflowException Then
MessageBox("The stack has overflowed!")
End If
Thanks Greg. Then I’m not sure why I’m getting an exception in this method:
Public Function FolderItemNotNilAndExistsMF(FileOrFolder As FolderItem) As Boolean
if FileOrFolder <> nil then
if FileOrFolder.Exists then
Return True
end if
end if
End Function
Should I put an exception handler around the 5 lines of code? Do you know of an instance where an exception could occur with the code in this method?
My guess, since the rest of the calls are identical and the exception is a StackOverflowException that when the method in question is called, the stack is already one call away from failure.
Over and over? That method is calling back into itself. If it does that too many times, one more call may be all it takes for the exception to occur. Any idea how many nested levels you were in when the exception was raised?
It’s been a while, but as I recall, the stack size is fixed for the main thread, but you can set the StackSize on a thread you call. It seemed like I could overflow the main stack pretty easily, so putting the recursive code into a thread and bumping the stack size resolved the problem.
The StackSize can only be set when the Thread is not running. The default stack size is 512KB on macOS and Linux and 1MB on Windows. A StackSize of zero indicates the default is used.
There is generally little reason to change this. Each stack frame only need to contain information about the parameters, local variables and return address for a method call. This does not include memory needed for objects. It would take a significant amount of local variables or parameters with method calls that are 100s of levels deep in order to exhaust the stack space.
No idea how many nested calls there are. This would depend on the folder structure of the users system.
All the method ‘FolderItemCountChildrenWithProgBarCheck’ does is count the number of files in the passed folder. If the passed folder contains a sub-folder, it calls itself with that sub-folder. I use the result of this to set the max level of a progress bar before scanning all WAV files in the tree.
This method is called from a thread, so I’m guessing the next best thing to do is see how many times it’s calling itself for this user. Then possibly look at changing the StackSize as per @Tim_Hare suggestion or re-think how I do this.
If the folder structure is too deep you will eventually get a stack overflow error. It should be relatively easy to redesign your function to avoid this.
You also need to make sure that a sub folder isn’t an alias to one of its parent folders otherwise you will never finish.
Public Function ScanTree(RootFolder As FolderItem, Extension As String, Status As DesktopLabel) As FolderItem()
Dim Folders() As FolderItem
Folders.Add(RootFolder)
Dim Files() As FolderItem
Do Until Folders.Count = 0
Var CurrentFolder As FolderItem = Folders(0)
Folders.RemoveAt(0)
Status.Text = CurrentFolder.NativePath
For Each Child As FolderItem In CurrentFolder.Children
If Child.IsFolder Then
If Child.IsReadable Then
Folders.Add(Child)
End If
Else
If Child.Name.EndsWith(Extension) Then
Files.Add(Child)
End If
End If
Next
Loop
Return Files
End Function
It doesn’t provide a total for progress bar purposes, but it does show the folder being scanned which gives some comfort to the user.
Thanks @kevin_g. I’ll re-write to stop the function calling itself and also check for an alias pointing to a parent folder. Good point. Thanks @Wayne_Golding for the sample code. This is a good place for me to start.
Using FileListMBS to list items in a folder is much faster. FileListMBS can also yield a bit so that going through a lot of files doesn’t show the beachball as fast.