Defining the Plug-In Configuration

The first step is to determine what items will appear in the plug-in configuration. The configuration is stored as an XML fragment in the help file builder project file. The root node is always configuration. Define your own elements to contain the plug-in configuration that will be nested within the root element. When first added to a project, the configuration will be empty. Your plug-in should use appropriate default values as needed. It is possible that a plug-in will have no configurable elements. In that case, there is nothing to add to the default configuration. Below is an example of a configuration that is passed to a plug-in.

CopyExample Plug-In Configuration
<configuration>
    <ajaxDoc url="http://localhost:/AjaxDoc/" project="MicrosoftAjax" regenerate="true" />
    <userCredentials useDefault="true" userName="" password="" />
    <proxyCredentials useProxy="false" proxyServer="">
        <userCredentials useDefault="true" userName="" password="" />
    </proxyCredentials>
</configuration>

Creating the Project

This section describes how to create and configure the build process plug-in project. It will describe the process for a C# project but the steps should be fairly similar for a VB.NET project with a few differences in the configuration option titles.

Create the Plug-In Project

  1. In Visual Studio, create a new Class Library project. Once it has been created, right click on the project and select Properties.

  2. In the Application tab, set the assembly name and default namespace as you see fit.

  3. In the Build Events tab, set the Post-build event command line to the following command based on your operating system which will copy the plug-in to the shared application data .\Components and Plug-Ins folder ready for testing (lines wrapped for display purposes):

    CopyWindows Vista/Windows 7
    Copy /y "$(TargetPath)" "%ProgramData%\EWSoftware\Sandcastle Help File
    Builder\Components and Plug-Ins\$(TargetName).plugins"
    CopyWindows XP
    Copy /y "$(TargetPath)" "%ALLUSERSPROFILE%\Application Data\
    EWSoftware\Sandcastle Help File Builder\Components and Plug-Ins\
    $(TargetName).plugins"
    Note

    The plug-in assembly must use the extension .plugins for it to be found by the help file builder.

    Add additional commands to copy any dependency assemblies and related configuration files as needed.

  4. On the Debug tab, set the Start Action to "Start external program" and enter the path to the SandcastleBuilderGUI.exe program. If you installed the help file builder in the default location, this will be C:\Program Files\EWSoftware\Sandcastle Help File Builder\. If not, substitute your path as appropriate.

  5. Select the Signing tab and check the "Sign the assembly" checkbox. Select "<New...>" from the "Choose a strong name key file" dropdown, enter a filename, and click OK to create the key file. You can protect the key file with a password if you like or uncheck the option to create one without a password.

The next task is to add the necessary assembly references.

Adding Assembly References

  1. Right click on the References folder in the project and select "Add Reference...".

  2. Select the Browse tab. Navigate to your Sandcastle Help File Builder installation folder (usually C:\Program Files\EWSoftware\Sandcastle Help File Builder\). Select SandcastleBuilder.Utils.dll and click OK to add it to the references. This contains the base class for the plug-in and some supporting definitions. Once added, view its properties and set the Copy Local option to False. This file is loaded from the help file builder installation folder and should not be copied locally or distributed with the plug-ins.

  3. If you would like to make your plug-ins interactively configurable from the Sandcastle Help File Builder, open the Add Reference dialog and add a reference to System.Windows.Forms from the .NET tab. More information about this will be supplied later.

You can add other necessary references to the project as you develop your plug-in.

The Plug-In Skeleton Class

You are now ready to create the plug-in class itself. Note that multiple plug-ins can reside within the same assembly. Repeat this process for as many plug-ins as you need to create giving each one a unique name. Add a new class to the project and insert the following skeleton code into it. Here again, this will only show C# code. For VB.NET, the structure will be similar but you will have to convert it to the VB.NET syntax. Each part will be covered in detail below.

CopyPlug-In Skeleton
//=============================================================================
// System  : <Your Plug-In Project>
// File    : <YourFileName.cs>
// Author  : <Your Name Here>
// Updated : <--Date-->
// Compiler: Microsoft Visual C#
// 
// <A description of the plug-in.>
// 
// Version     Date     Who  Comments
// ============================================================================
// 1.0.0.0  <--Date-->  <->  Created the code
//=============================================================================

using System;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Forms;
using System.Xml.XPath;

using SandcastleBuilder.Utils;
using SandcastleBuilder.Utils.BuildEngine;
using SandcastleBuilder.Utils.PlugIn;

// TODO: Use your namespace
namespace Test.PlugIns
{
    /// <summary>
    /// TODO: Set your plug-in's class name
    /// </summary>
    public class TestPlugIn : SandcastleBuilder.Utils.PlugIn.IPlugIn
    {
-        #region Private data members
         //=====================================================================
 
         private ExecutionPointCollection executionPoints;
 
         private BuildProcess builder;
         #endregion

-        #region IPlugIn implementation
         //=====================================================================
 
         /// <summary>
         /// This read-only property returns a friendly name for the plug-in
         /// </summary>
         public string Name
         {
             get { return "TODO: Add your plug-in name here"; }
         }
 
         /// <summary>
         /// This read-only property returns the version of the plug-in
         /// </summary>
         public Version Version
         {
             get
             {
                 // TODO: Edit AssemblyInfo.cs to set your plug-in's version
 
                 // Use the assembly version
                 Assembly asm = Assembly.GetExecutingAssembly();
                 FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(
                     asm.Location);
 
                 return new Version(fvi.ProductVersion);
             }
         }
 
         /// <summary>
         /// This read-only property returns the copyright information for the
         /// plug-in.
         /// </summary>
         public string Copyright
         {
             get
             {
                 // TODO: Edit AssemblyInfo.cs to set your plug-in's copyright
 
                 // Use the assembly copyright
                 Assembly asm = Assembly.GetExecutingAssembly();
                 AssemblyCopyrightAttribute copyright =
                     (AssemblyCopyrightAttribute)Attribute.GetCustomAttribute(
                         asm, typeof(AssemblyCopyrightAttribute));
 
                 return copyright.Copyright;
             }
         }
 
         /// <summary>
         /// This read-only property returns a brief description of the plug-in
         /// </summary>
         public string Description
         {
             get
             {
                 return "TODO: Add your plug-in description here";
             }
         }
 
         /// <summary>
         /// This read-only property returns true if the plug-in should run in
         /// a partial build or false if it should not.
         /// </summary>
         /// <value>If this returns false, the plug-in will not be loaded when
         /// a partial build is performed.</value>
         public bool RunsInPartialBuild
         {
             // TODO: Set this to true if necessary
             get { return false; }
         }
 
         /// <summary>
         /// This read-only property returns a collection of execution points
         /// that define when the plug-in should be invoked during the build
         /// process.
         /// </summary>
         public ExecutionPointCollection ExecutionPoints
         {
             get
             {
                 if(executionPoints == null)
                     executionPoints = new ExecutionPointCollection
                     {
                         // TODO: Modify this to set your execution points
                         new ExecutionPoint(
                             BuildStep.ValidatingDocumentationSources,
                             ExecutionBehaviors.Before),
                         new ExecutionPoint(
                             BuildStep.GenerateReflectionInfo,
                             ExecutionBehaviors.Before)
                     };
 
                 return executionPoints;
             }
         }
 
         /// <summary>
         /// This method is used by the Sandcastle Help File Builder to let the
         /// plug-in perform its own configuration.
         /// </summary>
         /// <param name="project">A reference to the active project</param>
         /// <param name="currentConfig">The current configuration XML fragment</param>
         /// <returns>A string containing the new configuration XML fragment</returns>
         /// <remarks>The configuration data will be stored in the help file
         /// builder project.</remarks>
         public string ConfigurePlugIn(SandcastleProject project,
           string currentConfig)
         {
             // TODO: Add and invoke a configuration dialog if you need one.
             MessageBox.Show("This plug-in has no configurable settings",
                 "Build Process Plug-In", MessageBoxButtons.OK,
                 MessageBoxIcon.Information);
 
             return currentConfig;
         }
 
         /// <summary>
         /// This method is used to initialize the plug-in at the start of the
         /// build process.
         /// </summary>
         /// <param name="buildProcess">A reference to the current build
         /// process.</param>
         /// <param name="configuration">The configuration data that the plug-in
         /// should use to initialize itself.</param>
         public void Initialize(BuildProcess buildProcess,
           XPathNavigator configuration)
         {
             builder = buildProcess;
 
             builder.ReportProgress("{0} Version {1}\r\n{2}\r\n",
                 this.Name, this.Version, this.Copyright);
 
             // TODO: Add your initialization code here such as reading the
             // configuration data.
         }
 
         /// <summary>
         /// This method is used to execute the plug-in during the build process
         /// </summary>
         /// <param name="context">The current execution context</param>
         public void Execute(ExecutionContext context)
         {
             // TODO: Add your execution code here
         }
         #endregion

-        #region IDisposable implementation
         //=====================================================================
 
         /// <summary>
         /// This handles garbage collection to ensure proper disposal of the
         /// plug-in if not done explicity with <see cref="Dispose()"/>.
         /// </summary>
         ~TestPlugIn()
         {
             // TODO: Change the name of this method to your plug-in name
             this.Dispose(false);
         }
 
         /// <summary>
         /// This implements the Dispose() interface to properly dispose of
         /// the plug-in object.
         /// </summary>
         /// <overloads>There are two overloads for this method.</overloads>
         public void Dispose()
         {
             this.Dispose(true);
             GC.SuppressFinalize(this);
         }
 
         /// <summary>
         /// This can be overridden by derived classes to add their own
         /// disposal code if necessary.
         /// </summary>
         /// <param name="disposing">Pass true to dispose of the managed
         /// and unmanaged resources or false to just dispose of the
         /// unmanaged resources.</param>
         protected virtual void Dispose(bool disposing)
         {
             // TODO: Dispose of any resources here if necessary
         }
         #endregion
    }
}

The plug-in is derived from the SandcastleBuilder.Utils.PlugIn..::..IPlugIn interface and consists of several properties and methods that you must implement. These are described below. Review the code above for "TODO:" comments to find sections that need attention such as changing the namespace, class name, etc. If you followed the steps in the Creating the Project section, you can run the project and debug it by setting breakpoints in the plug-in's code.

The SandcastleBuilder.Utils.PlugIn.IPlugIn Interface

The interface consists of a set of descriptive properties (Name, Version, Copyright, and Description) that provide basic information about the plug-in. The following properties and methods are used to configure the plug-in and execute it during a build.

RunsInPartialBuild

The RunsInPartialBuild property determines whether or not the plug-in runs during a partial build. Partial builds occur when generating the table of contents for the preview dialog and the API Filter editor dialog. In such cases, the plug-in may not be needed. If this property returns false, the plug-in is omitted which can speed up the partial build.

ExecutionPoints

The ExecutionPoints property is probably the most important member of the interface. It returns a collection of ExecutionPoint objects that define at which steps in the build process the plug-in should be ran. Execution points can be set to run before and/or after a build step to supplement the default processing or they can be set to run instead of the build step to completely supress or replace the default processing. See the BuildStep enumeration for a list of the defined build steps.

Each execution point can be assigned a priority. The execution priority is used to determine the order in which the plug-ins will be executed. Those with a higher priority value will be executed before those with a lower value. Those with an identical priority may be executed in any order within their group. If not specified, a default priority of 1,000 is used.

ConfigurePlugIn

The ConfigurePlugIn method is used to configure the plug-in interactively from within the help file builder. The method is passed a string containing the XML fragment that defines the current configuration. It should return a copy of the edited configuration.

Initialize

The Initialize method is used to initialize the plug-in at the start of the build process. It is passed a reference to the current BuildProcess and an XPath navigator containing the plug-in configuration. You should cache a copy of the build process reference for use during execution as it contains many properties and methods that you will find useful.

Execute

The Execute method is called to perform the plug-in processing during the relevant build steps. It is passed an SandcastleBuilder.Utils.PlugIn..::..ExecutionContext object that defines the current execution context. If your plug-in determines that it does not need to run, it should set the ExecutionContext..::..Executed property to false. This is especially important for plug-ins with the InsteadOf behavior. If none run, the default processing will occur.

Dispose

The Dispose method can be used to dispose of any resources used during the build process. If you do not have any resources that need to be disposed of, this method can be left blank.

Execution Notes

There are certain conditions to be aware of when choosing and coding the execution behavior for your plug-in.

  • The earliest execution point for a plug-in is the After behavior with the Initializing step.
  • The Before and InsteadOf behaviors cannot be used with the Initializing, Canceled, or Failed steps.
  • The InsteadOf behavior cannot be used with the FindingTools or Completed steps.
  • The InsteadOf behavior always takes precedence. The build process will not call any plug-ins with Before or After behavior for the step if an InsteadOf plug-in runs. As such, it is up to the plug-in with the InsteadOf behavior to call the ExecuteBeforeStepPlugIns and ExecuteAfterStepPlugIns methods to run them if needed.
  • If the build step involves creating, modifying, or deleting a non-script file or folder, the Before behavior is always executed prior to creating/modifying/deleting the file or folder. Use the After behavior if you need to guarantee that the file or folder exists or you do not want your version overwritten. The exceptions to this rule are MSBuild project files (*.proj) and MRefBuilder.config which are always created prior to running the Before behavior plug-ins. This allows you to modify the script files prior to them being ran.
  • If the build step involves creating, modifying, or deleting a script file or folder and the InsteadOf behavior is used, the plug-in is responsible for creating the script file/folder. All normal processing involving the file/folder including its creation is skipped.
  • The GenerateHelpFormatTableOfContents, GenerateHelpFileIndex, GenerateHelpProject, UpdateTableOfContents, and CompilingHelpFile steps will run once for each help file format selected. You can use the CurrentFormat property to determine the current help file format being built in order to skip or alter the plug-in's processing based on the help file format.
  • Be aware that the HTML Help 1 index and table of contents files and the website table of contents file are actually generated as part of the ExtractHtmlInfo step. However, the before and after plug-in behaviors for the index and table of contents steps in each of those formats will still be executed.

See Also

Other Resources