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.

Word document from code

Sometimes you may want to generate a Word document from code in D365FO, which gives you much more control over the result than if you simply printed a report to Word.

Here is a very brief example of how you can do it.

Start with creating an X++ project. Then add a C# class library to the same solution (I called it WordLib). Right-click the C# project, use Manage NuGet Packages… and install DocumentFormat.OpenXml package.

Then add the following class:

using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
 
namespace WordLib
{
    public class WordDoc
    {
        public Stream Create()
        {
            MemoryStream ms = new MemoryStream();
 
            using (WordprocessingDocument wordDocument =
                WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document, true))
            {
                MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();
 
                string text = "Do androids dream of electric sheep?";
                Body body = new Body(new Paragraph(new Run(new Text(text))));
 
                mainPart.Document = new Document(body);
            }
 
            return ms;
        }
    }
}

This code will create a very simple Word document, containing only a single line of text, and returns it as a memory stream. You would likely need something more complicated, but that’s out of scope of this blog post. You can get more information from Open XML SDK documentation.

Build the C# class library and we’re done with it; now we need to call it from X++.

Go to the X++ project, right-click References, chose Add Reference… and add a project reference to the C# library.

Then add a runnable class with the following code, which merely calls the library and returns the stream as a file to user:

class WordDocGeneratorSample
{        
    public static void main(Args _args)
    {     
        using (System.IO.Stream wordStream = new WordLib.WordDoc().Create())
        {
            File::SendFileToUser(wordStream, 'file.docx');
        }
    }
}

Set the class as the startup object, run the project and your browser should offer you the Word document for download.

Report printed to e-mail

You can easily send a D365FO report in email just by configuring print destination to Email and setting a few properties.

You can do the same from code, where you can also set the body. You must create an instance of SrsReportEMailDataContract class, fill it values and pass it to SRSPrintDestinationSettings.parmEMailContract(). Here is an example:

SrsReportRunController controller = new SrsReportRunController();
 
controller.parmReportName(ssrsReportStr(SysUserRoleInfo, Report));
controller.parmShowDialog(false);
 
SRSPrintDestinationSettings settings = controller.parmReportContract().parmPrintSettings();
 
settings.printMediumType(SRSPrintMediumType::Email);
settings.fileFormat(SRSReportFileFormat::PDF);
settings.fileName('SysUserRoleInfo.xlsx');
 
// Here we configure the email
SrsReportEMailDataContract emailContract = new SrsReportEMailDataContract();
 
emailContract.parmTo("user@example.com");
emailContract.parmSubject("Security report");
emailContract.parmBody("Hi there! Here is your report.");
 
settings.parmEMailContract(emailContract);
 
controller.startOperation();

Printing reports from code in D365FO

A comment below my blog post Printing reports from code in AX2012 asked me to provide an example for D365FO. Here is it.

The code is virtually identical. Only writing a file to a shared folder doesn’t make a good sense in cloud, therefore I changed the code to return the file to user for download.

SrsReportRunController          controller = new SrsReportRunController();
SysUserLicenseCountRDPContract  rdpContract = new SysUserLicenseCountRDPContract();
SRSPrintDestinationSettings     settings;
 
// Define report and report design to use
controller.parmReportName(ssrsReportStr(SysUserLicenseCountReport, Report));
// Use execution mode appropriate to your situation
controller.parmExecutionMode(SysOperationExecutionMode::Synchronous);
// Suppress report dialog
controller.parmShowDialog(false);
 
// Explicitly provide all required parameters
rdpContract.parmReportStateDate(systemDateGet());
controller.parmReportContract().parmRdpContract(rdpContract);
 
// Change print settings as needed
settings = controller.parmReportContract().parmPrintSettings();
settings.printMediumType(SRSPrintMediumType::File);
settings.fileFormat(SRSReportFileFormat::Excel);
settings.fileName('UserLicenseCount.xlsx');
 
// Execute the report
controller.startOperation();

Dynamics 365 Saturday South Africa

I’m safely back in Prague and I would like to thank everybody who attended Dynamics 365 Saturday in Johannesburg. There were so many of you!

And I want to give special thanks to people who did all the work to make the event happen and those who cared of me there. This was my first visit to South Africa and it was great you showed my lions, local food and everything.

I hope that my talks (about .NET in D365FO and about design for extensibility) gave people some useful information.

Dynamics 365 Saturday isn’t limited to South Africa, of course. There are quite a few already scheduled, therefore check what’s going in your area. It’s all free!