in

ljusberg.se

Smöråkning

August 2006 - Posts

  • Moving

    I've recently begun the process of moving my domains to a new ISP, and the new one (Axentus) gives me access to a SQL Server 2005. I've installed Community Server on it, and hopefully I'll manage to move this blog to the CS blog engine. I'll try my best to make the transition as smooth as possible, but it's not a very easy process..

  • Sandcastle August CTP Released

    The team behind Microsoft Sandcastle released a new CTP of Sandcastle yesterday. A couple of changes have been made to the components that will make the previous version of my MSBuild Script for Sandcastle fail. Therefore, you'll need to download the latest version of the scripts if you want it to work with the August CTP.

    New features include:

    • Support for live links to MSDN documentation.
    • Index for CHM-builds.
    • Frontpage for CHM-builds.

    Installation instructions are the same as before! And as always, your comments are appreciated!

  • Sandcastle Help Compiler 2 support

    Update

    After installing the August CPT of Sandcastle, you'll need to update to the latest version of the MSBuild script.

    I've just released a new version of the MSBuild-script for Sandcastle. There are two new features and one new (though optional) requirement.

    First of all, the new requirement is the Visual Studio .NET Help Integration Kit 2003 which contains Microsoft Help Compiler 2.

    The two new features of the script are:

    • Can now compile both CHM (Html Help 1.x) and HxS (Microsoft Help 2) files
    • Supports custom content files (things like header, footer and copyright text)

    You can download the following files:

    • Sandcastle.zip
      Unzip the contents of this file to c:\Program Files\MSBuild\Sandcastle.
    • SandcastleTest.zip
      This file contains a sample solution and a sample MSBuild script (called Documentation.proj).

    Instructions are more or less the same as before:

    1. If you haven't already done so, install the Sandcastle June CTP. The scripts rely on a completly unmodified installation, so don't make any changes to it (especially not the sandcastle.config file).
    2. Unzip the contents of Sandcastle.zip to your MSBuild folder (usually c:\Program Files\MSBuild)
    3. Unzip the contents of SandcastleTest.zip to wherever you like to keep your sample projects.
    4. Open up the SandcastleTest.sln solution file and build the project.
    5. Open a Visual Studio 2005 Command prompt, navigate to the SandcastleTest folder and run the one of the following commands:
      • MSBuild Documentation.proj
        To build a CHM file.
      • MSBuild Documentation.proj /t:DocumentationNew
        To build an HxS file.
      • MSBuild Documentation.proj /t:DocumentationAll
        To build both a CHM and an HxS file
    6. Cross your fingers, and when the process is finished, look for the Output\SandcastleTest.chm and/or the Output\SandcastleTest.HxS files!
    7. In order to actually view an HxS-file you'll need to register it with MSHelp. Or, an easier way (just to see that it works) is to use the free H2Viewer-utility.

  • XML improvements in .NET Framework 2.0

    I just found this rather interesting article on MSDN that describes what's new in the System.Xml namespace for .NET Framework 2.0. The main points I took note of where:
    • Always use the new factory methods for creating XmlReaders and XmlWriters (i.e. XmlReader.Create and XmlWriter.Create)
    • Use XslCompiledTransform instead of XslTransform. Apparently, it compiles your XSLT into MSIL code, which is way cool if you ask me!
    • The ReadSubtree()-method on XmlReader looks interesting. It's always been a bit of a pain navigating an Xml document with just the Read()-method.
    Posted Aug 14 2006, 02:04 PM by anders with no comments
    Filed under: ,
  • Sandcastle under the hood, part 3

    Welcome to part 3 of my Sandcastle under the hood-series. In part 2, which focused on all the different components available to use in the BuildAssembler, I promised you a walkthrough of the sandcastle.config that was delivered in the July CTP of Sandcastle. So here we go!

    Input to BuildAssembler

    First of all a reminder of the files we can work with (some of them created in the first steps):

    • reflection.xml
      Contains reflected data from all assemblies
    • manifest.xml
      A list of all topics.
    • commments.xml (or similar)
      One or more files that contain the xml comments assembled from your source code. These files are produced by using the /doc switch when compiling your project.
    • sandcastle.config
      The file where we define which components to run during the BuildAssembler process, their input parameters et.c.

    Also available are a bunch of xml files with reflection data for most common .NET Framework assemblies (found in Examples\Cpref_reflection). And if you have the .NET Framework SDK installed (you probably do if you're a developer), you also have the xml comments for those assemblies (found in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727).

    Running BuildAssembler with sandcastle.config

    Remember, the BuildAssembler runs once for each topic found in the manifest.xml. The input for each run is the topic id, which can look something like "P:SandcastleTestLibrary.TestClass.MyTestString".

    First, the CopyFromFileComponent is used to bring in a skeleton xml document. Nothing very fancy, it simply looks like this:

    <document>
    <reference />
    <syntax />
    <comments />
    </document>

    In the initialization phase, the next CopyFromIndexComponent has built up an index (called "reflection") of all reflection xml files (reflection.xml and Examples\Cpref_reflection\*.xml). It uses the id attribute of the &ltapi> elements as unique identifiers. This unique identifier is matched to the current topic id, and the contents of that node is copied into the document/reference element.

    The next two CopyFromIndexComponents use the same index ("reflection") to insert api information about the namspace and/or type that contains the current topic. Everything except the elements node is copied. So what previously looked like this:

    <reference>
    <apidata name="MyTestString" group="member" subgroup="property" />
    ..
    <containers>
    <library assembly="SandcastleTestLibrary" module="SandcastleTestLibrary" />
    <namespace api="N:SandcastleTestLibrary" />
    <type api="T:SandcastleTestLibrary.TestClass" ref="true" />
    </containers>
    </reference>

    Turns into this:

    <reference>
    <apidata name="MyTestString" group="member" subgroup="property" />
    ..
    <containers>
    <library assembly="SandcastleTestLibrary" module="SandcastleTestLibrary" />
    <namespace api="N:SandcastleTestLibrary">
    <apidata name="SandcastleTestLibrary" group="namespace" />
    <file name="f9552800-d515-86c9-d25b-6cdb3d52a8be" />
    </namespace>
    <type api="T:SandcastleTestLibrary.TestClass" ref="true">
    <apidata name="TestClass" group="type" subgroup="class" />
    <typedata visibility="public" abstract="false" sealed="false" serializable="false" layout="auto" format="ansi" />
    <family>
    <ancestors>
    <type api="T:System.Object" ref="true" />
    </ancestors>
    </family>
    <containers>
    <library assembly="SandcastleTestLibrary" module="SandcastleTestLibrary" />
    <namespace api="N:SandcastleTestLibrary" />
    </containers>
    <file name="0ee38ce4-f243-9c7b-5521-2106d1b1f4bc" />
    </type>
    </containers>
    </reference>

    Next, an IfThenComponent is used to make sure that the next step is only applied to topics that are not Overloads. For all other topics, a SyntaxComponent is used to generate the syntax strings. One string is generated for each language. The strings are placed in the <syntax> element and look something like this:

    <syntax>
    <div codeLanguage="CSharp">
    <span class="keyword">public</span> <referenceLink target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false">string</referenceLink> <span class="identifier">MyTestString</span> { get; set; }</div>
    <div codeLanguage="VisualBasic">
    <span class="keyword">Public</span> <span class="keyword">Property</span> <span class="identifier">MyTestString</span> <span class="keyword">As</span> </div target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false">
    <div codeLanguage="ManagedCPlusPlus">
    <span class="keyword">public</span>:
    <referenceLink target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false" />^ <span class="identifier">MyTestString</span> {
    <referenceLink target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false" />^ <span class="keyword">get</span> ();
    <span class="keyword">void</span> <span class="keyword">set</span> (, <referenceLink target="T:System.String" prefer-overload="false" show-container="false" show-templates="false" show-parameters="false" />^ <span class="parameter">value</span>);
    }</div>
    </syntax>

    A new CopyFromIndexComponent has built up an index of all files containing xml comments. This index is called "comments" and is at first used to copy in the comments for the current topic.

    Now, a ForEachComponent loops through all contained elements. For each such element, the reflected info and comments for this element are copied from the previously created "reflection" and "comments" indexes. The document now contains all data that is needed for the topic!

    <document>
    <reference>
    <apidata name="TestClass" group="type" subgroup="class" />
    ..
    <elements>
    ..
    <element api="P:SandcastleTestLibrary.TestClass.MyTestString">
    <apidata name="MyTestString" group="member" subgroup="property" />
    ..
    <file name="ffff1456-643b-ea6a-547a-9ce4ebc205a4" />
    <summary>
    A test property
    </summary>
    </element>
    ..
    </elements>
    ..
    <file name="0ee38ce4-f243-9c7b-5521-2106d1b1f4bc" />
    </reference>
    <syntax>
    ..
    </syntax>
    <comments>
    <summary>
    This is the summary documentation for TestClass
    </summary>
    </comments>
    </document>

    It's time for the big transformation! A TransformComponent applies the Presentation\transforms\main_sandcastle.xsl template file to the current document. The result is a complete html, but it still contains a couple of custom nodes that isn't part of a the standard HTML specification.

    The SharedContentComponent is used to replace any links to, you guessed it, shared content. Among other things, this is where the header and footer is applied. You can change it to something more to your liking by editing the file Presentation\Content\shared_content.xml.

    Finally, the ResolveReferenceLinksComponent is used to create correct links to internal and external documents.

    In the end, the now complete document is written to disc using the GUID filename that was generated in one of the first steps.

    Next steps

    I could describe the last steps of the process as well (generating a TOC, running HHC etc) but to be honest, if you've followed along all the way to this point you probably already understand the rest. Instead, my next step will be to make the MSBuild script work with Microsoft Help Compiler 2.0 too.

    As always, I really do appreciate any comments you have, so fire away!

    Update

    Also check out Part 1 and Part 2

    Posted Aug 10 2006, 11:30 AM by anders with 1 comment(s)
    Filed under:
  • Sandcastle under the hood, part 2

    In my previous post, I talked you through the first steps of the Sandcastle build process. So far it's been more or less simple xsl transforms, but now we're getting to the fun part - the BuildAssembler.

    The BuildAssembler is actually just a small console application that runs an XML document through a configurable pipeline of components. The pipeline is run once for each topic in the manifest. It is sandcastle.config that tells the BuildAssembler which components to apply to the XML document, and in which order they are to be applied. Before running the first topic, each component gets a chance to initialize itself according to the parameters given.

    There's a whole bunch of different components you can use. I've compiled a list of them and a short description of what (I think) they do.

    Content components

    These components are primarily used to add new content to the XML document.

    CopyFromFileComponent

    Adds the contents of a file to the XML document

    CopyFromIndexComponent

    Copies specific parts of the contents of one or more XML files to the XML document. When initializing, this component builds up an index of the set of XML-files which makes the process rather quick. The index is global to the entire process so the same index can be used by multiple CopyFromIndexComponents.

    Flow components

    Components used for controlling the flow through the pipeline.

    ForEachComponent

    This component typically contains one or more other components. It applies these contained components on each node in a node list given by an xpath expression.

    IfThenComponent

    Evaluates an xpath expression. Depending on the result it either applies the components in the then-branch or the else-branch.

    SwitchComponent

    Evaluates an xpath expression. Depending on the value it applies the appropriate components in the correct case-branch.

    Output components

    DisplayComponent

    Dumps the contents of the current XML document to the console. Check out a previous post to see how you can take advantage of this!

    SaveComponent

    Saves the contents of the current XML document to a file.

    ValidateComponent

    Validates the current document against one or more XML Schema (xsd) files. Any errors are written to the log (usually just the console).

    Transformation components

    These components do the main workload. They resolve references, replace nodes, transform, create new structures et.c.

    ResolveReferenceLinksComponent

    Resolves links to other pages in the current project or to pages in external documents. There are three kinds of links available; Local (within the current project, creates <a href= style links), Index (to other MShelp documents, creates MSHelp-style links) and None (just text, doesn't produce a link at all).

    SyntaxComponent

    Used together with one or more SyntaxGenerators (there is one for each main language) to create syntax strings for the current class/method/property et.c. The syntax generators actually seem complex enough to warrant an entire article of their own.

    SharedContentComponent

    Replaces <include> and <includeAttribute> elements with contents found in external xml files (like shared_content.xml and reference_content.xml). The content can also take parameters in the standard form expected by string.Format, so if the item in shared_content.xml looks like this:

    <item id="artPath">..\art\{0}</item>
    Then a node like this:
    <img>
    <includeAttribute name="src" item="artPath">
    <parameter>LastChild.gif</parameter>
    </includeAttribute>
    </img>
    Will be transformed into this:
    </img src="..\art\LastChild.gif" temp_src="..\art\LastChild.gif">

    TransformComponent

    Simply applies an xsl transform to the document.

    Other components

    These are components that I'm not really sure about how they work since they aren't used in the example given.

    ExampleComponent

    Seems to be used to include a set of common sample code snippets. Not sure, but I think it looks for comment links with the following signature: <ddue:codeReference>

    ResolveArtComponent

    Seems to be used for linking to common image files?

    ResolveConceptualLinksComponent

    Looks quite similar to the ResolveReferenceLinksComponent, and I'm not really sure about the difference.

    Well, this turned out to be a rather long post. In the next session, I'll explain how the sandcastle.config is currently wired to build the sample project

    Update

    Also check out Part 1 and Part 3

    Posted Aug 05 2006, 11:06 AM by anders with no comments
    Filed under:
  • Sandcastle under the hood, part 1

    Well, today I've been diving into the innards of Sandcastle. In lack of proper documentation I've relied on ildasm, some trace output and some common sense. Now, I don't know how much of this that is actually correct, but I suspect that at least most of it is rather close to the truth.

    Assuming you already have your compiled assemblies and documentation files the process begins with MRefBuilder. The MRefBuilder builds up an xml file by reflecting the assemblies specified on the command line. The output contains <assembly>-elements and <api>-elements. The assembly elements simply contain some meta-data about each assembly (version, description, company name et.c.). The api elements are more interesting and are categorized in groups and subgroups. Some examples of groups/subgroups are:

    GroupSubgroup
    root
    namespace
    typeclass
    typeenumeration
    memberconstructor
    membermethod
    memberproperty
    memberfield

    Many api elements have links to other elements (i.e. a class can contain constructors, properties and methods). Some api elements are marked as pseudo="true", this means that they have no direct equivalent element in the actual code. The original reflection file is enriched by applying two xsl transforms. The first one, AddOverloads.xsl, creates pseudo api-elements for grouping any overloaded methods together.

    Before AddOverloads:

    <api id="M:SandcastleTestLibrary.TestClass.TestOverload(System.Int32)">
    ...
    </api>
    <api id="M:SandcastleTestLibrary.TestClass.TestOverload(System.String)">
    ...
    </api>

    After AddOverloads:

    <api id="Overload:SandcastleTestLibrary.TestClass.TestOverload">
    ...
    <elements>
    <element api="M:SandcastleTestLibrary.TestClass.TestOverload(System.Int32)" />
    <element api="M:SandcastleTestLibrary.TestClass.TestOverload(System.String)" />
    </elements>
    </api>
    <api id="M:SandcastleTestLibrary.TestClass.TestOverload(System.Int32)">
    ...
    </api>
    <api id="M:SandcastleTestLibrary.TestClass.TestOverload(System.String)">
    ...
    </api>

    The other transform, AddGuidFilenames.xsl, uses a hash of the unique id for each api element to create a filename in the form of a guid, this will be the filename of the final .htm file.

    Next, a manifest file is created, again using an xsl transform. For each api element in the reflection.xml, a topic element is created in the manifest. The manifest, together with the sandcastle.config file is what drives the BuildAssembler. But more on that in my next post. Stay tuned for more!

    Update

    Also check out Part 2 and Part 3

    Posted Aug 04 2006, 11:20 PM by anders with no comments
    Filed under:
  • Sandcastle behind the scenes

    Are you, as I am, curious about what actually goes on behind the scenes in the Sandcastle BuildAssembler? Well, here's a tip that will give you data enough to investigate it the entire weekend!

    Open up your sandcastle.config file and add the following line between each <component> node:

    <component type="Microsoft.Ddue.Tools.DisplayComponent" assembly="..\..\ProductionTools\BuildComponents\BuildComponents.dll"/>

    This component will make the BuildAssembler print out the current state of the XML-file it's currently working on each time it hits it, so you can see how the XML progresses through each step. I just tried this with a medium-sized project of ours, and ended up with a 11 Mb sized log file.. Fun for the whole family! :-)

    Anyway, stay tuned for more findings..

    Posted Aug 04 2006, 05:14 PM by anders with no comments
    Filed under:
  • Sandcastle Visual Studio Project

    Final update

    Sorry, I'm not able to figure out what's wrong with this, and since Frank has a better solution for Visual Studio integration, I'm cancelling this attempt. If you're interested in the source code, send me an email! I think it's better if I focus my efforts on the MSBuild script instead.

    Update 3

    Have a look at Frank Kroondijk's Sandcastle plugin. It seems to work much better (at least looking at the video - I haven't installed it yet).

    Update 2

    It seems there is still a bug of some sort that gives you an error message when creating a project. I'd recommend that you hold off on installing this until I can find a solution.

    Update

    There was a file missing in the msi-package, so you'll have to uninstall the old version and repeat steps 2 and 3 below in order for it to work properly.

    As I hinted in yesterdays post, I've been working on a Visual Studio plugin for the Sandcastle MSBuild-script, and today I'm happy enough with it to share it with the community. Be warned though, this is my first attempt at a VS project plugin, and it is most likely rather unstable so please realize the risks of installing it (worst-case scenario, you'll have a broken Visual Studio).. That said, it works ok for me and uninstalling it seems to be fine too.

    Installation instructions:

    1. Prerequisites: Visual Studio 2005 (Standard edition or better) and an unmodified version of Sandcastle.
    2. You'll need the latest version (updated a few minutes ago) of the Sandcastle MSBuild-script.
    3. Download and install the SandcastleProjectSetup.msi-package.
    4. Open up a Visual Studio 2005 Command Prompt (you'll find it on the Start menu under your Visual Studio Tools-folder) and run the following command:
      devenv.exe /setup

    Usage:

    After installing the package, a new Project type is available from the New/Add project dialog. Typically, you'll add this project to an existing solution.

    In order to bring up the main settings page, you simpy right-click the newly added project and select Properties. This is where you select which assemblies and documentation files you wish to include. You can include files from the current solution by clicking the Select button, or external files by clicking the Add button.

    Finally, to actually build your documentation file, right click the project and select "Build". The documentation will (if everything works as planned) end up in the a subfolder to the documentation project called Output.

    If you get an error from the build engine, the best way to find out what's wrong is to run the build outside of Visual Studio. You do that simply by running MSBuild on the [ProjectName].sandproj file. The most common error is probably an Unresolved Assembly Reference. Currently, you can only add dependencies manually to the .sandproj file. You'll have to unload the project to do so.

    Comments are always appreciated! If you're interested in the (not-so-pretty) source code for this, why not send me an email! You'll need the Visual Studio SDK (April 2006) installed for it to compile though. The address is: andlju (at) gmail (dot) com.

  • Teaser: Sandcastle integrated in Visual Studio

    As you may have seen in previous posts from me, I've created a MSBuild script that makes it easier to create Sandcastle documentation files. But hey, why stop there? Wouldn't it be nice if the whole process was integrated in Visual Studio like all other products? Well, I couldn't help myself, so I tried it and it kinda' works.

    I'm not releasing it just yet, because there are quite a few things to work out before it's ready for public use - but here are two teaser screenshots for you to look at in the mean-time.