Find D365FO labels in Powershell

You can use Dynamics 365 > Find labels… in Visual Studio to find D365FO labels, but its capabilities are quite limited. Sometimes a better option is searching through the underlying text files directly and you can easily automate it with Powershell.

The following script looks at all label files for US English and finds all labels containing either PowerBI or Power BI. It skips label comments, because you’re typically interested only in labels and not their comments.

$packagesDir = 'C:\AOSService\PackagesLocalDirectory\'
$labelFiles = ls $packagesDir\*\*\AxLabelFile\LabelResources\en-US\*.txt
$labelFiles | Select-String -Pattern '^(?! ;).*=.*Power ?BI' | select Line, Filename, LineNumber

You might think this approach will be slow, but it actually returns result very quickly.

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();