UserGuide

Exception Handling

From Xojo Documentation

The debugger can help you verify that your code is working as you expect and it can help you find errors. Once you’ve found the source of errors, you want to make sure you handle them properly.

Exceptions are a type of error that occur when something unexpected happens. These errors will crash your application if you do not handle them in some way. The act of causing an exception to occur is called raising an exception.

All exception are subclasses of the RuntimeException class.

When an exception is encountered in your code, you can choose to have the Debugger displayed at the line causing the exception. To do this, select Project ↠ Break On Exceptions in the menu so that it has a checkmark next to it.

Since exceptions are for unexpected behavior, if you are able to avoid an error by preventing the exception from occurring then it is more efficient to do so. One reason is that when testing, the Debugger is displayed for any event that is raised if you have "Break On Exceptions" enabled in the Project menu. For example, with a Dictionary you use the Value method to fetch a value based on a key. If the key is not found you'll get a KeyNotFoundException. But you can avoid that by using either the Dictionary.Lookup (to apply a default if the key is not found) or the Dictionary.HasKey methods. So if you do expect situations where a key may not be found then you can account for it with these methods. And since that is not something unexpected, relying on the KeyNotFoundException is unnecessary.

NilObject Exceptions

The most common type of exception that occurs is a NilObjectException. This exception occurs when you attempt to use an object but don’t have an instance of it. If a NilObjectException occurs in your built app, then a dialog appears:

If you are running your app in debug mode from within the Xojo IDE, then first the Debugger appears showing you the line of code that caused the exception and you can see the exception in the Variables pane of the Debugger. When you Resume, the above dialog will be displayed if your app does not handle the exception. If your app handles the exception then the exception handler runs and your code continues.

The simplest example of how you can get a NilObjectException is forgetting to use New to get an instance before you try to use a property or call a method on it:

Var d As Dictionary
d.Value("ID") = "Test" // NilObjectException

The above code raises a NilObjectException because you did not use New to create a Dictionary instance (or an object). The correct way to write this code is with the New command:

Var d As New Xojo.Core.Dictionary
d.Value("ID") = "Test" // NilObjectException

Another place to check for this is in the return value of methods that return an instance. Some methods return Nil in certain situations, such as GetOpenFolderItem which returns Nil if the user clicks Cancel:

Var f As FolderItem
f = FolderItem.ShowOpenFileDialog("")
If f.Exists Then // NilObjectException
// Do something
End If

To prevent these types of errors, which is preferred, you should always check if the value is Nil before you attempt to access it:

Var f As FolderItem
f = FolderItem.ShowOpenFileDialog("")
If f <> Nil Then
If f.Exists Then
// Do something
End If

End If

Try Catch

Sometimes you may find that an exception is a normal part of processing. What you want to do in these situations is catch the exception so that you can deal with it appropriately rather than letting your app crash. The Try...Catch command is used for this purpose.

For example, loading an XML file can raise an XMLException if the file is not valid XML. You can check for this exception by using a Try..Catch block:

Var xmlFile As FolderItem
xmlFile = FolderItem.ShowOpenFileDialog("")
If xmlFile <> Nil Then
Try
Dim xml As New XmlDocument
xml.LoadXml(xmlFile)
Catch e As XmlException
MessageBox("Not an XML file!")
Return
End Try
End If
// Process the XML

If no XMLException is raised then the code in after the End Try section runs. If an XMLException is raised, then the code in the Try block stops running and the code in the Catch block is run, which in this case displays a message and allows the app to continue running.

When you are running in debug mode, the Debugger appears when the exception is raised, even if you have code to handle the exception (when Project ↠ Break On Exceptions is turned on). Choose Resume to have your app continue running.

If you'd like to avoid the Debugger from appearing in specific situations you can temporarily turn this behavior off by using a Pragma:

#Pragma BreakOnExceptions Off
Try
// your code
Catch e As NilObjectException
// handle exception
End Try
#Pragma BreakOnExceptions Default // Restore setting from Project menu

Exception

The Exception command is a simplified version of Try...Catch. Rather than focusing on a specific block of code, Exception catches errors for the entire method.

The Exception command goes at the end of the method and is called if an exception is generated anywhere in the method. The preceding example could be written like this using the Exception command:

Var xmlFile As FolderItem
xmlFile = FolderItem.ShowOpenFileDialog("")
If xmlFile <> Nil Then
Var xml As New XmlDocument
xml.LoadXml(xmlFile)
End If
// Process the XML

Exception e As XmlException
MessageBox("Not an XML file!")
Return

Although this is simpler, it is not as obvious what is occurring (especially if there is even more code in the method) and it is also far less flexible since it only works on the entire method. In most cases you should use Try...Catch.

App.UnhandledException

There is a special event handler in the App object of your project that is called if an unhandled exception is not caught using Try...Catch or Exception.

The event has one parameter, error, which is the exception itself. You can put code in this event handler to display a message to the user, capture log information or anything else you want. Return True to hide the default unhandled exception error dialog and attempt to allow you application to continue, but this is not recommended because your app may no longer be stable depending on what caused the exception.

Desktop Apps

This code in App.UnhandledException displays a friendlier message to the user and then quits the application:

Var msg As String = "An error occurred. Please notify the author."
MessageBox(msg)

Quit
Return True

You can also display the runtime stack so it can be used to help pinpoint the location of the problem so you can fix it:

Var msg As String = "An error occurred. Please notify the author. Stack: "
MessageBox(msg + error.Stack.FromArray(EndOfLine))

Quit
Return True

Web Apps

The default error dialog displays information about the error and provides a field for users to add additional information if App.UnhandledException is not implemented or returns False. This information is written to an errors.log file alongside the web app. If the dialog that is displayed, if the user clicked "Send" you can also get the information in the WebSession.JavaScriptError event handler so that you can log it yourself.

Remember, if this event handler has been called and you return False (the default) it means your entire web app is going to stop running. Return True to hide the default error dialog and prevent the web app from quitting.

iOS Apps

iOS apps do not display an error dialog for unhandled exceptions; the app just terminates. You'll need to add your own code to App.UnhandledException to display unhandled exceptions.

Creating Your Own Exceptions

Since RuntimeException is a class like any other, you can subclass it to create your own exceptions. This allows you to raise your own exceptions that you have defined.

If you create a RuntimeException subclass called InvalidXMLFileFormatException then you can raise it using this syntax:

If incorrectXmlFileFormat Then
Raise New InvalidXMLFileFormatException
End If

See Also

Try...Catch, Exception commands; RuntimeException class; Runtime Exceptions category; UserGuide:Debugger Usage topic