First chance exceptions

Dynamics 365 for Finance and Operations runs completely in “.NET”, which has some interesting implications. The runtime and tools working with it (such as Visual Studio) offer many new options that may be very useful.

One of these options is the ability to catch first chance exceptions. When an exception is thrown, it’s considered a first chance exception. Visual Studio won’t break the program execution, because your program still has a chance to catch the exception and do something about it. Only if it doesn’t do that and the exception is unhandled, Visual Studio will break the program. It doesn’t happen in D365FO at all, because it has a default handler for all exceptions.

Therefore if I call code like this and never catch the exception, D365FO will still catch it and put a message to infolog.

System.IO.File::OpenRead("c:\\nothing_here");

This is the result:

Now what if I you want to debug the code to see what happened? This error message contains a stack trace, therefore you can put breakpoint to the method throwing the error and run the logic again.

But often you don’t have the call stack; you have only the exception type and message:

System.IO.FileNotFoundException: Could not find file 'c:\nothing_here'.

This happens when there is an exception handler in the application that handles the error and merely puts a message to infolog. Or if it’s an X++ exception, because D365FO doesn’t show stack trace for them.

You can’t use breakpoints because you don’t know where you should put them. Wouldn’t it be great if Visual Studio interrupted execution immediately when the exception is thrown, i.e. at the first chance exception? Fortunately there is a way!

Go to Visual Studio and open Debug > Windows > Exception Settings. Expand Common Language Runtime Exceptions, find FileNotFoundException (you can use the search box on the top) and tick the checkbox.

When you run the code again (with debugging), Visual Studio breaks the program when the exception is thrown and shows the dialog which is normally used for unhandled exceptions:

The example above used a .NET exception, FileNotFoundException. But can we use something like that with native X++ exceptions? Yes, we can!

I’ll use the following piece of code for my example:

HrmCompCreateGrid::construct().run();

If I run the code, I’ll get the following messages in infolog.

I can’t merely put try/catch to my code, because there is no unhandled exception. The exception is already handled in some code called from run().

But I can ask Visual Studio to break on first chance exceptions.

X++ doesn’t use objects for exceptions; throwing an error means throwing an enum value (throw Exception::Error). But D365FO uses CLR (“.NET runtime”) and must play but its rules, therefore every X++ exception is converted to an instance of an exception class under the hood. The class is Microsoft.Dynamics.Ax.Xpp.ErrorException.

If you open Debug > Windows > Exception Settings again, you’ll notice that Microsoft.Dynamics.Ax.Xpp.ErrorException isn’t in the list of Common Language Runtime Exceptions. But it doesn’t matter, because you can add it there.

Select Common Language Runtime Exceptions node and click the green plus button:

Type in Microsoft.Dynamics.Ax.Xpp.ErrorException

and press Enter. The exception is now in the list and Visual Studio will break every time when an X++ error exception is thrown.

When you run the demo code again (with debugging), Visual Studio will stop at the throw statement in createGrid() method.

Finding the place where an exception is thrown may be time consuming, especially if it’s handled somewhere but you don’t know where. This little trick may save you a huge amount of time.

It also shows that there is a lot to gain from the new platform and from Visual Studio, if you use them creatively.

Note that things like this are also applicable to AX 2012, if X++ code is executed as CIL (which, unlike in D365FO, happens only in some cases). I sometimes intentionally ran code in CIL just to get access to features that aren’t available in the X++ debugger.