Sandcastle

Introduction
Sandcastle documentation compilers enable managed class library developers throughout the world to easily create accurate, informative documentation with a common look and feel. With Dynamics AX, one can create XML documentation files that can be compiled with Sandcastle to create CHM help files.

An [[media:ShellExecute.chm.zip|example]] of Dynamics AX XML documentation compiled with Sandcastle:



Required Software
Microsoft .NET Framework Version 2.0, available here

HTML Help Workshop available here

Sandcastle available here or here

Steps

 * 1) Creating the XML documentation files
 * 2) Modifying the generated reflection file (reflection.org)
 * 3) Optional additions
 * 4) Compiling Sandcastle

Creating the XML documentation files
Follow the steps described in the Dynamics AX 2009 SDK to generate the XML documentation files. Be sure to store both the documentation and reflection file in the same folder and name them as follows:

comments.xml    (documentation file)

reflection.org  (reflection file)

For XML documentation tags compatible with AX and Sandcastle, see: XML Documentation Tags

Modifying the generated reflection file (reflection.org)
In order for Sandcastle to create the table of contents and generate the html files, the following changes need to be made to the reflection file:
 * Replace the first occurance of  with 
 * Replace every occurance of   with  

This could be done directly in AX with the following changes: This transformation could also be done with the following xsl file and XmlTransform::execute:
 * In SysDictionary.xmlReflection, add _xmlWriter.writeAttributeString('id', 'N:'); after _xmlWriter.writeStartElement(#XmlApi);
 * In SysDictionary.xmlReflectionApiData, replace root with namespace
 * In SysDictClass.xmlReflectionContainers and SysDictMethod.xmlReflectionContainers, insert above the last occurance of _xmlWriter.writeEndElement; the following: _xmlWriter.writeStartElement('namespace'); _xmlWriter.writeAttributeString('api', 'N:'); _xmlWriter.writeEndElement;

      N:      <xsl:if test="self::apidata and @pseudo"> namespace</xsl:attribute> </xsl:if> <xsl:if test="self::containers"> <xsl:element name="namespace"> N:</xsl:attribute> </xsl:element> </xsl:if> </xsl:copy> </xsl:template> </xsl:stylesheet>

Here is an example of transforming an XML file with the XmlTransform class: void transform(FilePath _xsl, FilePath _old, FilePath _new) {   #XmlDocumentation

XmlTextWriter writer = XmlTextWriter::newFile(_new); ;

writer.writeProcessingInstruction(#XmlXml, #XmlVersion);

writer.writeRaw(XmlTransform::execute(XmlReader::newFile(       _xsl), XmlReader::newFile(_old), true));

writer.flush; }

Assembly information
To display assembly information, add a library element to each containers element with assembly and module attributes.


 * Replace every occurance of   with <library assembly="Microsoft Dynamics AX" module="Ax32" /> 

This does the same with xsl:

<xsl:template match="node|@*"> <xsl:copy> <xsl:if test="self::containers"> <xsl:element name="library" use-attribute-sets="library"> Microsoft Dynamics AX</xsl:attribute> Ax32</xsl:attribute> </xsl:element> </xsl:if> <xsl:apply-templates select="node|@*"/> </xsl:copy> </xsl:template>

The result might look like this:

<type api="R:Project" /> <library assembly="Microsoft Dynamics AX" module="Ax32" /> <namespace api="N:" />

Base enums
When base enums are added to a project, Sandcastle crashes with the follow error:

''Error: BuildAssembler: An error occured while initializing the build component 'Microsoft.Ddue.Tools.ResolveReferenceLinksComponent2' in the component assembly 'C:\Program Files\Sandcastle\ProductionTools\BuildComponents.dll'. The error message and stack trace follows: System.ArgumentNullException: Value cannot be null.''

Solution:


 * Replace every occurance of "enumerationentry" /> with "enumerationentry" /> <type api="R:Project" /> 

This does the same with xsl: <xsl:template match="node|@*"> <xsl:copy> <xsl:if test="child::apidata[@subgroup='enumerationentry']"> <xsl:element name="containers"> <xsl:element name="type"> R:Project</xsl:attribute> </xsl:element> </xsl:element> </xsl:if> <xsl:apply-templates select="node|@*"/> </xsl:copy> </xsl:template>

The result should look similar to this:

<api id="F:Ax::IsCool"> <apidata name="IsCool" group="member" subgroup="enumerationentry" /> <typedata value="0" label="IsCool" configurationkey="" /> <type api="R:Project" />

Extended data types
Extended data types are not supported, so define them as a class.


 * Replace every occurance of subgroup="extendeddatatype" with subgroup="[class|structure|interface|enumeration|delegate]"

Or, doing the same with xsl (this example replaces table and relation or index with class and field):

<xsl:template match="node|@*"> <xsl:copy> <xsl:if test="self::apidata[@subgroup='extendeddatatype']"> class</xsl:attribute> </xsl:if> <xsl:apply-templates select="node|@*"/> </xsl:copy> </xsl:template>

The result should look similar to this:

Hiding other languages
To prevent other languages from being displayed, such as VB or C++, edit the presentation sandcastle.config file and comment out all of the undesired elements.

Example: <generator type="Microsoft.Ddue.Tools.CSharpDeclarationSyntaxGenerator" assembly="%DXROOT%\ProductionTools\SyntaxComponents.dll" />

Tables
Tables, relations and indexes are not supported with Sandcastle, so if they are included in an AX project, then they need to be changed to one of the supported types, such as class and field.


 * Replace every occurance of subgroup="table" with subgroup="[class|structure|interface|enumeration|delegate]"
 * Replace every occurance of subgroup="relation" with subgroup="[constructor|method|property|field|event]"
 * Replace every occurance of subgroup="index" with subgroup="[constructor|method|property|field|event]"

Or, doing the same with xsl (this example replaces table and relation or index with class and field):

<xsl:template match="node|@*"> <xsl:copy> <xsl:if test="self::apidata[@subgroup='table']"> class</xsl:attribute> </xsl:if> <xsl:if test="self::apidata[@subgroup='relation']         or self::apidata[@subgroup='index']          or self::apidata[@subgroup='fieldgroup']"> field</xsl:attribute> </xsl:if> <xsl:apply-templates select="node|@*"/> </xsl:copy> </xsl:template>

The result should look similar to this:

<api id="M:MyTable.fields.Name"> <apidata name="Name" group="member" subgroup="field" /> <memberdata id="50001" configurationkey="" mandatory="false" persisted="true" /> <type api="T:MyTable" /> <library assembly="Microsoft Dynamics AX" module="Ax32" /> <namespace api="N:" /> <type api="T:Name" /> Name Name.

Namespace
To give the namespace a name, add the "name" attribute to the first "apidata" element and give it a value, such as "AOT":

<xsl:if test="self::apidata and @pseudo"> namespace</xsl:attribute> AOT</xsl:attribute> </xsl:if>

The result should look similar to this:

<?xml version="1.0" encoding="utf-8"?> <reflection build="5.0.1500.1313" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:ddue="urn:ddue-extensions"> <api id="N:" api="R:Project"> <apidata pseudo="true" group="namespace" name="AOT" />

X++ language syntax title
For X++ one can use the C# generator since the syntax is similar. However, for the documentation to be titled as X++ rather than C#, modify shared_content.xml in the Sandcastle presentation content folder and replace <item id="CSharpLabel">C# with <item id="CSharpLabel">X++.

Example:

<item id="CSharpLabel">X++ <item id="VisualBasicLabel">Visual Basic <item id="VisualBasicUsageLabel">Visual Basic Usage <item id="ManagedCPlusPlusLabel">Visual C++

Compiling Sandcastle
"Creating a Chm build using Sandcastle" walks one through the process of manually creating a chm file using Sandcastle. For AX, jump to step 5. More examples can be found in the Sandcastle examples folder under %programfiles%\Sandcastle\Examples. To simplify the process, I created [[media:RunSandcastle.zip|RunSandcastle.bat]]. Download and extract it to the XML documentation folder and then run the following command from dos to generate help in the "chm" subfolder:

[[media:RunSandcastle.zip|RunSandcastle.bat]] [Presentation] [Output file title]

Example:

C:\Temp\RunSandcastle.bat Hana ShellExecute