Click here to Skip to main content
15,895,557 members
Articles / Programming Languages / Forth.NET

DocMounter 2: A tool to build VS.NET documentation (now with Sandcastle)

,
Rate me:
Please Sign up or sign in to vote.
4.94/5 (29 votes)
15 Nov 2010GPL314 min read 139.5K   1.4K   99  
Tool for creating MS Visual Studio documentation files - XML Summaries, HxS/MSHC help solutions and manuals.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using MSHelpCompiler;
using TenTec.Utils;

namespace SandCastleHelpBuilder
{
	/// <summary>
	/// Builds MSHelp2 files based on XML and MAML descriptions.
	/// </summary>
	public class SandCastleHelpBuilder: IHelpBuilder
	{
		#region Consts

		#region ParseInputDataConstants

		/// <summary>
		/// Represents the constants used to parse data passed from the called tool.
		/// </summary>
		private static class ParseInputDataConstants
		{
			/// <summary>
			/// The string which is used to separate doc set attrbibutes.
			/// </summary>
			public const char cDocSetListDelimiter = ',';
		}

		#endregion

		#region MSHelp2BuildProcessConstants

		/// <summary>
		/// Represents the constants related to the HxS build process
		/// </summary>
		private static class MSHelp2BuildProcessConstants
		{
			/// <summary>
			/// The extension of the compiled help file.
			/// </summary>
			public const string cHelpSolutionExtension = ".HxS";

			/// <summary>
			/// The relative path to the directory where MSHelp2 template files are located.
			/// </summary>
			public const string cTemplatesRelativeDirectoryPath = "MSHelp2Templates";

			/// <summary>
			/// The name of the MSHelp2 project file.
			/// </summary>
			public const string cHxCFileName = "MSHelp2.HxC";

			/// <summary>
			/// The name of the intermediate Toc file.
			/// </summary>
			public const string cTocFileName = "toc.xml";
		}

		#endregion

		#region ReflectionFilesConstants

		/// <summary>
		/// Represents the constants related to SandCastle's MRefBuilder utility
		/// used to create the reflection file for documented assemblies.
		/// </summary>
		private static class ReflectionFilesConstants
		{
			/// <summary>
			/// The name of a temporary reflection file.
			/// </summary>
			public const string cReflection1FileName = "reflection.org1.xml";

			/// <summary>
			/// The name of a temporary reflection file.
			/// </summary>
			public const string cReflection2FileName = "reflection.org2.xml";

			/// <summary>
			/// The name of a temporary reflection file.
			/// </summary>
			public const string cReflection3FileName = "reflection.org3.xml";

			/// <summary>
			/// The name of the reflection file.
			/// </summary>
			public const string cReflectionFileName = "reflection.xml";
		}

		#endregion

		#region MediaContentsConstants

		/// <summary>
		/// Represents the constants related to the SandCastle media contents file 
		/// (it stores the list of all the images used in conceptual topics)
		/// </summary>
		private static class MediaContentsConstants
		{
			/// <summary>
			/// The name of the file.
			/// </summary>
			public const string cName = "MediaContent.xml";

			/// <summary>
			/// The name of the root node.
			/// </summary>
			public const string cRootNodeName = @"stockSharedContentDefinitions";

			/// <summary>
			/// The name of the node which stores information about a single media file.
			/// </summary>
			public const string cItemNodeName = "item";

			/// <summary>
			/// The name of the attribute which stores the id of an media file.
			/// </summary>
			public const string cIdAttributeName = "id";

			/// <summary>
			/// The name of the node which stores information about an image file.
			/// </summary>
			public const string cImageNodeName = "image";

			/// <summary>
			/// The name of the attribute which stores the name of an image file.
			/// </summary>
			public const string cFileAttributeName = "file";
		}

		#endregion

		#region MSHelp2HxFFileConstants

		/// <summary>
		/// Representa the constants related to the MSHelp2 'file list' file.
		/// </summary>
		private static class MSHelp2HxFFileConstants
		{
			/// <summary>
			/// The name of the MSHelp2 'file list' file.
			/// </summary>
			public const string cName = "MSHelp2.HxF";

			/// <summary>
			/// The x-path to the node in the MSHelp2 HxF file which is the parent for all the file nodes.
			/// </summary>
			public const string cFileListNodeXPath = @"HelpFileList";

			/// <summary>
			/// The name of the node which stores information about a single file.
			/// </summary>
			public const string cFileNodeName = "File";

			/// <summary>
			/// The name of the attribute which stores location of a file.
			/// </summary>
			public const string cUrlAttributeName = "Url";
		} 

		#endregion

		#region MsHelp2HxTFileConstants

		/// <summary>
		/// Represents constants related to the MSHelp2 TOC file (HxT).
		/// </summary>
		private static class MsHelp2HxTFileConstants
		{
			/// <summary>
			/// The name of the MSHelp2 Toc file.
			/// </summary>
			public const string cName = "MSHelp2.HxT";

			/// <summary>
			/// The root node of the document.
			/// </summary>
			public const string cRootNodeName = "HelpTOC";

			/// <summary>
			/// The name of the node which represents a node in contents tree.
			/// </summary>
			public const string cItemNodeName = "HelpTOCNode";

			/// <summary>
			/// The attribute which stores the link to a topic.
			/// </summary>
			public const string cUrlAttributeName = "Url";

			/// <summary>
			/// The attribute which stores a topic title.
			/// </summary>
			public const string cTitleAttributeName = "Title";
		} 

		#endregion

		#region SandCastleSourceFilesConstants

		/// <summary>
		/// Represents constants related to sand castle source files.
		/// </summary>
		private static class SandCastleSourceFilesConstants
		{
			/// <summary>
			/// The name of the folder where comments for SandCastle are stored.
			/// </summary>
			public const string cSandCastleCommentsDirectoryRelativePath = "Comments";

			/// <summary>
			/// The path to a directory which should be present when compiling conceptual topics.
			/// </summary>
			public const string cSandCastleExtractedFilesDirectoryRelativePath = "ExtractedFiles";

			/// <summary>
			/// The path to a directory which should be present when compiling conceptual topics.
			/// </summary>
			public const string cSandCastleXmlCompDirectoryRelativePath = "XmlComp";

			/// <summary>
			/// The relative path to the directory where SandCastle template 
			/// files are located (conceptual topics configuration file).
			/// </summary>
			public const string cSandCastleTemplateRelativeDirectoryPath = "SandCastleTemplates";

			/// <summary>
			/// The path to a directory where source media files for SandCastle are stored.
			/// </summary>
			public const string cSandCastleMediaDirectoryRelativePath = "Media";

			/// <summary>
			/// The relative path to the presentation icons directory.
			/// </summary>
			public const string cIconsDirectoryRelativePath = "Icons";

			/// <summary>
			/// The relative path to the presentation scripts directory.
			/// </summary>
			public const string cScriptsDirectoryRelativePath = "Scripts";

			/// <summary>
			/// The relative path to the presentation styles directory.
			/// </summary>
			public const string cStylesDirectoryRelativePath = "Styles";

			/// <summary>
			/// The relative path to the directory where SandCastle creates HTML files.
			/// </summary>
			public const string cHtmlDirectoryRelativePath = "Html";

			/// <summary>
			/// The relative path to the file which stores information about files
			/// which should be included into MsHelp2 help.
			/// </summary>
			public const string cSeedHxFRelativePath = "seed.HxF";

			/// <summary>
			/// The relative path to the feedBack_content.xml file 
			/// (it contains information about the feedback link).
			/// </summary>
			public const string cFeedbackContentsRelativePath = @"Content\feedBack_content.xml";

			/// <summary>
			/// The relative path to the utilities_metadata.xsl file 
			/// (it contains a part of topic transformation scheme).
			/// </summary>
			public const string cUtilitiesMetadataTransformationRelativePath = @"Transforms\utilities_metadata.xsl";

			/// <summary>
			/// The relative path to the main_conceptual.xsl file 
			/// (it contains a part of topic transformation scheme).
			/// </summary>
			public const string cMainConceptualTransformationRelativePath = @"Transforms\main_conceptual.xsl";

			/// <summary>
			/// The relative path to the presentation configuration files directory.
			/// </summary>
			public const string cSandCastleConfigFilesDirectoryRelativePath = "Configuration";

			/// <summary>
			/// The relative path to the MRefBuilder utility.
			/// </summary>
			public const string cMRefBuilderRelativePath = @"ProductionTools\MRefBuilder.exe";

			/// <summary>
			/// The relative path to the XSLTransform utility.
			/// </summary>
			public const string cXSLTransformRelativePath = @"ProductionTools\XSLTransform.exe";

			/// <summary>
			/// The relative path to the BuildAssembler utility.
			/// </summary>
			public const string cBuildAssemblerRelativePath = @"ProductionTools\BuildAssembler.exe";

			/// <summary>
			/// The name of the SandCastle config file.
			/// </summary>
			public const string cMemberTopicsSandCastleConfigFileName = @"sandcastle.config";

			/// <summary>
			/// The name of the SandCastle config file for conceptual topics.
			/// </summary>
			public const string cConceptualTopicsSandCastleConfigFileName = @"conceptual.config";

			/// <summary>
			/// Relative path of the MergeDuplicates xslt transformation.
			/// </summary>
			public const string cMergeDuplicatesRelativePath = @"ProductionTransforms\MergeDuplicates.xsl";

			/// <summary>
			/// Relative path of the CreateVSToc xslt transformation.
			/// </summary>
			public const string cCreateVSTocRelativePath = @"ProductionTransforms\CreateVSToc.xsl";

			/// <summary>
			/// Relative path of the TocToHxSContents xslt transformation.
			/// </summary>
			public const string cTocToHxSContentsRelativePath = @"ProductionTransforms\TocToHxSContents.xsl";

			/// <summary>
			/// Relative path of the ApplyVSDocModel xslt transformation.
			/// </summary>
			public const string cApplyVSDocModelRelativePath = @"ProductionTransforms\ApplyVSDocModel.xsl";

			/// <summary>
			/// Relative path of the AddGuidFilenames xslt transformation.
			/// </summary>
			public const string cAddGuidFilenamesRelativePath = @"ProductionTransforms\AddGuidFilenames.xsl";

			/// <summary>
			/// Relative path of the ReflectionToManifest xslt transformation.
			/// </summary>
			public const string cReflectionToManifestRelativePath = @"ProductionTransforms\ReflectionToManifest.xsl";
		} 
		
		#endregion

		#region SandCastleConfigXmlFileConstants

		/// <summary>
		/// Represents constants related to SandCastle configuration files.
		/// </summary>
		public static class SandCastleConfigXmlFileConstants
		{
			/// <summary>
			/// The x-path to the node in the SandCastle config file for member topics which stores the path to the XML comments.
			/// </summary>
			public const string cCommentsNodeXPath = @"configuration/dduetools/builder/components/component/index[@name='comments']/data";//[@files='.\comments.xml']";			

			/// <summary>
			/// The x-path to the node to insert MSHelp2 DocSet attributes data for the SHFB component.
			/// </summary>
			public const string cMSHelp2AttributesNodeXPath = @"/configuration/dduetools/builder/components/component[@id='MS Help 2 Attributes']";

			/// <summary>
			/// The name of the 'attributes' node to insert MSHelp2 DocSet attributes data for the SHFB component.
			/// </summary>
			public const string cAttributesNodeName = "attributes";

			/// <summary>
			/// The name of the 'attribute' node to insert MSHelp2 DocSet attribute data for the SHFB component.
			/// </summary>
			public const string cAttributeNodeName = "attribute";

			/// <summary>
			/// The name of the 'name' attribute to insert MSHelp2 DocSet attribute data for the SHFB component.
			/// </summary>
			public const string cNameAttributeName = "name";

			/// <summary>
			/// The value of the 'name' attribute to insert MSHelp2 DocSet attribute data for the SHFB component.
			/// </summary>
			public const string cNameAttributeValue = "DocSet";

			/// <summary>
			/// The name of the 'value' attribute to insert MSHelp2 DocSet attribute data for the SHFB component.
			/// </summary>
			public const string cValueAttributeName = "value";

			/// <summary>
			/// The name of the XML attribute which specifies file locations
			/// in a sand castle configuration file.
			/// </summary>
			public const string cFilesAttributeName = "files";

			/// <summary>
			/// The filter for SandCastle comment files which is used in SandCastle config.
			/// </summary>
			public const string cCommentsFilter = "*.xml";
		}

		#endregion

		#region SandCastleOutputFilesConstants

		/// <summary>
		/// Represents the constants related to the SandCastle output files (HTML).
		/// </summary>
		private static class SandCastleOutputFilesConstants
		{
			/// <summary>
			/// The name of the folder which is created in the output folder and used to store temporary files.
			/// </summary>
			public const string cMainBuildTempDirectoryName = "TempBuild_HxS";

			/// <summary>
			/// The name of the folder where SandCastle presentation files are stored.
			/// </summary>
			public const string cSandCastleOutputDirectoryRelativePath = "Output";

			/// <summary>
			/// The extension of output html files (with dot).
			/// </summary>
			public const string cHtmlFileExtension = ".htm";

			/// <summary>
			/// The relative path to the directory where output html files are stored.
			/// </summary>
			public const string cHtmlDirectoryRelativePath = SandCastleSourceFilesConstants.cHtmlDirectoryRelativePath;
		}

		#endregion

		#region SharedContentXmlFileConstants

		/// <summary>
		/// Represents constants related to our custom shared content file.
		/// </summary>
		public static class SharedContentXmlFileConstants
		{
			/// <summary>
			/// The relative path to the SharedContentOverwrite.xml file 
			/// used to store common items for both member and conceptual topics
			/// (such as "running header text").
			/// </summary>
			public const string cSharedContentXmlFileName = @"SharedContentOverwrite.xml";

			/// <summary>
			/// The x-path to the node in our custom shared content file
			/// which contains the running header text
			/// </summary>
			public const string cRunningHeaderTextNodeXPath = @"content/item[@id='runningHeaderText']";
		}
	
		#endregion

		#region SandCastleFeedbackContentsXmlFileConstants

		/// <summary>
		/// Represents constants related to the SandCastle feedback content file.
		/// </summary>
		public static class SandCastleFeedbackContentsXmlFileConstants
		{
			/// <summary>
			/// The x-path to the node in the SandCastle feedback content file which locates
			/// the e-mail node.
			/// </summary>
			public const string cEmailNodeXPath = @"content/item[@id='fb_alias']";

			/// <summary>
			/// The x-path to the node in the SandCastle feedback content file which locates
			/// the product name node.
			/// </summary>
			public const string cProductNameNodeXPath = @"content/item[@id='fb_product']";

			/// <summary>
			/// The x-path to the node in the SandCastle feedback content file which locates
			/// the company node.
			/// </summary>
			public const string cCompanyNodeXPath = @"content/item[@id='fb_company']";

			/// <summary>
			/// The x-path to the node in the SandCastle feedback content file which locates
			/// the documentation version node.
			/// </summary>
			public const string cDocVersionNodeXPath = @"content/item[@id='fb_docversion']";	
		}

		#endregion

		#region SandCastleUtilitiesMetadataXslFileConstants

		/// <summary>
		/// Represents constants related to a SandCastle metadata XSL file.
		/// </summary>
		public static class SandCastleMetadataXslFileConstants
		{
			/// <summary>
			/// The x-path to the node in a SandCastle metadata XSL file which locates
			/// the medatada XML node.
			/// </summary>
			public const string cMetadataXmlNodeXPath = @"xsl:stylesheet/xsl:template[@name='insertMetadata']/xsl:if/xml";

			public const string cXslNamespacenamePrefix = "xsl";

			public const string cXslNamespacenameUri = "http://www.w3.org/1999/XSL/Transform";

			public const string cMsHelpNamespacePrefix = "MSHelp";

			public const string cMsHelpNamespaceUri = "http://msdn.microsoft.com/mshelp";

			public const string cAttributeNodeName = "Attr";

			public const string cNameAttributeName = "Name";

			public const string cValueAttributeName = "Value";

			public const string cDocSetName = "DocSet";
		}

		#endregion

		#region ManifestXmlFileConstants

		/// <summary>
		/// Represents constants related to sandcastle manifest file.
		/// </summary>
		private static class ManifestXmlFileConstants
		{
			/// <summary>
			/// The name of the manifest file for conceptual topics.
			/// </summary>
			public const string cConceptualTopicsManifestName = "conceptual_manifest.xml";

			/// <summary>
			/// The name of the manifest file for member topics.
			/// </summary>
			public const string cMemberTopicsManifestFileName = "manifest.xml";

			/// <summary>
			/// The name of the root node.
			/// </summary>
			public const string cRootNodeName = "topics";

			/// <summary>
			/// The name of the node which represents a topic.
			/// </summary>
			public const string cTopicNodeName = "topic";

			/// <summary>
			/// The attribute where topic id is stored.
			/// </summary>
			public const string cTopicIdAttributeName = "id";
		}		 

		#endregion

		#region XmlCompanionFileConstants

		/// <summary>
		/// Represents constants related to companion files which are created for every
		/// conceptual topic in order to specify topic titles which are used to display 
		/// references to the topic.
		/// </summary>
		private static class XmlCompanionFileConstants
		{
			/// <summary>
			/// The format string which is used to form a companion file name.
			/// The 0 parameter is the topic id.
			/// </summary>
			public const string cFileNameFormatString = "{0}.cmp.xml";

			/// <summary>
			/// The format string which is used to fill the contents of a companion file.
			/// The 0 parameter is the topic id. 
			/// The 1 parameter is the topic title.
			/// </summary>
			public const string cContentsFormatString = 
				"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + 
				"<metadata fileAssetGuid=\"{0}\" assetTypeId=\"CompanionFile\">\r\n" + 
				"  <topic id=\"{0}\">\r\n" + 
				"    <title>{1}</title>\r\n" +
				"    <tableOfContentsTitle>{1}</tableOfContentsTitle>\r\n" +
				"  </topic>\r\n" + 
				"</metadata>";
		}

		#endregion

		#region ConceptualTopicsMetadataFileConstants
		
		/// <summary>
		/// Represents constants used to produce the conceptual topics metadata file.
		/// </summary>
		private static class ConceptualTopicsMetadataFileConstants
		{
			/// <summary>
			/// The name of the conceptual topics metadata file.
			/// </summary>
			public const string cConceptualTopicsManifestName = @"ConceptualTopicsMetadata.xml";

			/// <summary>
			/// The name of the root node.
			/// </summary>
			public const string cRootNodeName = @"metadata";

			/// <summary>
			/// The name of the node which represents a topic.
			/// </summary>
			public const string cTopicNodeName = @"topic";

			/// <summary>
			/// The attribute where topic id is stored.
			/// </summary>
			public const string cTopicIdAttributeName = @"id";

			/// <summary>
			/// The node used as parameter to read "running header text" data.
			/// </summary>
			public const string cRunningHeaderTextElementRawString = @"<runningHeaderText uscid=""runningHeaderText"" />";
		}

		#endregion

		#region ExternalToolsConstants

		/// <summary>
		/// Represents constants used to refer external executable tools
		/// </summary>
		public static class ExternalToolsConstants
		{
			public const string cSandCastleToolSystemVar = "DXROOT";
			public const string cHxRegExeFileName = "hxreg.exe";
			public const string cDExploreExeFileName = "dexplore.exe";
		}

		#endregion

		#endregion

		#region Methods

		/// <summary>
		/// Builds an HxS help file
		/// (defined in the IHelpBuilder interface).
		/// </summary>
		public void BuildHelp(
			string[,] assemblyFiles,
			string outputDirPath,
			string mediaDirPath,
			string helpNamespace,
			string name,
			string title,
			string companyName,
			string version,
			string feedbackEmail,
			string docSetList,
			ConceptualTopic[] conceptualTopics,
			string conceptualTopicXmlsDirPath,
			ProcessStepDelegate processStepCallback,
			Func<bool> shouldStopCallback)
		{

			#region Initialize folders

			string
				myMainBuildTempDirectory,
				mySandCastleOutputDirectory,
				mySandCastleConfigFilePath,
				mySandCastleConceptualTopicsConfigFilePath,
				mySandCastleCommentsDirectory,
				mySandCastleConceptualTopicsCompanionFilesDirectory,
				myMsHelpTemplatesDirectoryPath,
				mySandCastleHtmlDirecotry,
				mySandCastleSeedFilePath,
				mySandCastleMediaDirectory;

			PrepareOutputStuff(
				processStepCallback,
				outputDirPath,
				assemblyFiles,
				conceptualTopics,
				mediaDirPath,
				feedbackEmail,
				name,
				title,
				companyName,
				version,
				docSetList,
				conceptualTopicXmlsDirPath,
				out myMainBuildTempDirectory,
				out mySandCastleOutputDirectory,
				out mySandCastleConfigFilePath,
				out mySandCastleConceptualTopicsConfigFilePath,
				out mySandCastleCommentsDirectory,
				out mySandCastleConceptualTopicsCompanionFilesDirectory,
				out mySandCastleSeedFilePath,
				out mySandCastleHtmlDirecotry,
				out myMsHelpTemplatesDirectoryPath,
				out mySandCastleMediaDirectory);

			#endregion

			try
			{
				#region Delete the output file

				string myHelpFilePath = GetHelpFilePath(outputDirPath, name);
				if (File.Exists(myHelpFilePath))
					File.Delete(myHelpFilePath);

				#endregion

				#region Create member topic HTMLs

				#region Run MRefBuilder

				// Prepare the first argument of MRefBuilder (dll list)
				StringBuilder myMRefBuilderDLLArgStringBuilder = new StringBuilder();
				for (int myFileIndex = 0; myFileIndex < assemblyFiles.GetLength(0); myFileIndex++)
				{
					string myAssemblyFilePath = assemblyFiles[myFileIndex, 0];
					myMRefBuilderDLLArgStringBuilder.Append("\"");
					myMRefBuilderDLLArgStringBuilder.Append(myAssemblyFilePath);
					myMRefBuilderDLLArgStringBuilder.Append("\" ");
				}

				// Run MRefBuilder to reflect over the set of our assemblies
				// and create an XML output file containing information about
				// the APIs found in those assemblies.
				RunProcess(
					processStepCallback,
					shouldStopCallback,
					Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cMRefBuilderRelativePath),
					string.Format(
						@"{0} ""/out:{1}\{2}""",
						myMRefBuilderDLLArgStringBuilder.ToString(),
						myMainBuildTempDirectory,
						ReflectionFilesConstants.cReflection1FileName),
						null,
						true,
						true,
						true,
						false);

				#endregion

				#region Run XSLTransforms

				RunProcess(
					processStepCallback,
					shouldStopCallback,
					Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cXSLTransformRelativePath),
					string.Format(
						@"""{0}\{1}"" ""/xsl:{2}"" ""/out:{0}\{3}""",
						myMainBuildTempDirectory,
						ReflectionFilesConstants.cReflection1FileName,
						Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cMergeDuplicatesRelativePath),
						ReflectionFilesConstants.cReflection2FileName),
						null,
						true,
						true,
						true,
						false);

				RunProcess(
					processStepCallback,
					shouldStopCallback,
					Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cXSLTransformRelativePath),
					string.Format(
						@"""{0}\{1}"" ""/xsl:{2}"" ""/out:{0}\{3}"" /arg:IncludeAllMembersTopic=true",
						myMainBuildTempDirectory,
						ReflectionFilesConstants.cReflection2FileName,
						Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cApplyVSDocModelRelativePath),
						ReflectionFilesConstants.cReflection3FileName),
						null,
						true,
						true,
						true,
						false);

				RunProcess(
					processStepCallback,
					shouldStopCallback,
					Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cXSLTransformRelativePath),
					string.Format(
						@"""{0}\{1}"" ""/xsl:{2}"" ""/out:{0}\{3}""",
						myMainBuildTempDirectory,
						ReflectionFilesConstants.cReflection3FileName,
						Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cAddGuidFilenamesRelativePath),
						ReflectionFilesConstants.cReflectionFileName),
						null,
						true,
						true,
						true,
						false);

				RunProcess(
					processStepCallback,
					shouldStopCallback,
					Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cXSLTransformRelativePath),
					string.Format(
						@"""{0}\{1}"" ""/xsl:{2}"" ""/out:{0}\{3}""",
						myMainBuildTempDirectory,
						ReflectionFilesConstants.cReflectionFileName,
						Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cReflectionToManifestRelativePath),
						ManifestXmlFileConstants.cMemberTopicsManifestFileName),
						null,
						true,
						true,
						true,
						false);

				#endregion

				#region Run BuildAssembler

				RunProcess(
					processStepCallback,
					shouldStopCallback,
					Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cBuildAssemblerRelativePath),
					string.Format(
						@"""{0}\{1}"" ""/config:{2}""",
						myMainBuildTempDirectory,
						ManifestXmlFileConstants.cMemberTopicsManifestFileName,
						mySandCastleConfigFilePath),
						myMainBuildTempDirectory,
						true,
						true,
						true,
						false);

				#endregion

				#endregion

				#region Create conceptual topics HTMLs

				if ((conceptualTopics != null) && (conceptualTopics.Length > 0))
				{
					#region Create conceptual topics metadata file

					CreateConceptualTopicsMetadataFile(myMainBuildTempDirectory, conceptualTopics);

					#endregion

					#region Create companion files

					CreateCompanionFiles(mySandCastleConceptualTopicsCompanionFilesDirectory, conceptualTopics);

					#endregion

					#region Create media contents file

					CreateMediaContentsFile(myMainBuildTempDirectory, mySandCastleMediaDirectory);

					#endregion

					#region Create manifest file

					string myConceptualTopicsManifestPath = CreateConceptualTopicManifestFile(conceptualTopics, myMainBuildTempDirectory);

					#endregion

					#region Run BuildAssembler

					RunProcess(
						processStepCallback,
						shouldStopCallback,
						Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cBuildAssemblerRelativePath),
						string.Format(
							@"""{0}"" ""/config:{1}""",
							myConceptualTopicsManifestPath,
							mySandCastleConceptualTopicsConfigFilePath),
						myMainBuildTempDirectory,
						true,
						true,
						true,
						false);

					#endregion
				}

				#endregion

				#region Copy media folder into sandcastle output folder

				// We do it to support images in member topics with the <img /> tag.

				string myOutputMediaDirectory = Path.Combine(
					mySandCastleOutputDirectory, 
					SandCastleSourceFilesConstants.cSandCastleMediaDirectoryRelativePath);

				CopyDirectory(mySandCastleMediaDirectory, myOutputMediaDirectory, true);

				#endregion

				#region Create MSHelp2 TOC

				#region Create TOC for the member topics

				RunProcess(
					processStepCallback,
					shouldStopCallback,
					Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cXSLTransformRelativePath),
					string.Format(
						@"""{0}\{1}"" ""/xsl:{2}"" ""/out:{0}\{3}""",
						myMainBuildTempDirectory,
						ReflectionFilesConstants.cReflectionFileName,
						Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cCreateVSTocRelativePath),
						MSHelp2BuildProcessConstants.cTocFileName),
					null,
					true,
					true,
					true,
					false);

				string myTocFilePath = Path.Combine(myMsHelpTemplatesDirectoryPath, MsHelp2HxTFileConstants.cName);

				RunProcess(
					processStepCallback,
					shouldStopCallback,
					Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cXSLTransformRelativePath),
					string.Format(
						@"""{0}\{1}"" ""/xsl:{2}"" ""/out:{3}""",
						myMainBuildTempDirectory,
						MSHelp2BuildProcessConstants.cTocFileName,
						Path.Combine(SandcastleToolPath, SandCastleSourceFilesConstants.cTocToHxSContentsRelativePath),
						myTocFilePath),
					null,
					true,
					true,
					true,
					false);

				#endregion

				#region Add conceptual topics to the TOC

				if (conceptualTopics != null && conceptualTopics.Length > 0)
					AddConceptualTopicsToMsHelp2HxTFile(myTocFilePath, conceptualTopics);

				#endregion

				#endregion

				#region Create MSHelp2 File List (HxF)

				string myHxFFilePath = Path.Combine(myMsHelpTemplatesDirectoryPath, MSHelp2HxFFileConstants.cName);
				FillMsHelp2HxFFile(
					mySandCastleSeedFilePath,
					mySandCastleHtmlDirecotry,
					mySandCastleOutputDirectory,
					mySandCastleMediaDirectory,
					myHxFFilePath);

				#endregion

				#region Compile MSHelp2

				if (processStepCallback != null)
					processStepCallback("Compiling HxS file...", false);

				HxCompClass myHxCompiler = new HxCompClass();
				myHxCompiler.Initialize();

				HxCompError myHxError = new HxCompError(processStepCallback);

				int myCookie = myHxCompiler.AdviseCompilerMessageCallback(myHxError);
				try
				{
					myHxCompiler.Compile(
						Path.Combine(myMsHelpTemplatesDirectoryPath, MSHelp2BuildProcessConstants.cHxCFileName),
						myMsHelpTemplatesDirectoryPath,
						myHelpFilePath,
						0);
				}
				finally
				{
					if (myCookie != -1)
						myHxCompiler.UnadviseCompilerMessageCallback(myCookie);
				}

				if (myHxError.IsFailed)
					throw new Exception(myHxError.ErrorMessage);

				#endregion
			}
			catch (BuildStoppedException)
			{ }

			#region Delete the temp folder

			if (GlobalDeleteIntermediateFiles)
				Directory.Delete(myMainBuildTempDirectory, true);
			
			#endregion

		}

		/// <summary>
		/// Creates a file which lists all the media files which are used in conceptual topics.
		/// </summary>
		private void CreateMediaContentsFile(string outputDirectoryPath, string sandCastleMediaDirectory)
		{
			string myFileName = Path.Combine(outputDirectoryPath, MediaContentsConstants.cName);
			using (XmlTextWriter myWriter = new XmlTextWriter(myFileName, Encoding.Default))
			{
				myWriter.Formatting = Formatting.Indented;

				myWriter.WriteStartElement(MediaContentsConstants.cRootNodeName);

				if (Directory.Exists(sandCastleMediaDirectory))
				{
					string[] myImageFiles = Directory.GetFiles(sandCastleMediaDirectory);
					if (myImageFiles != null && myImageFiles.Length > 0)
					{
						foreach (string myImageFile in myImageFiles)
						{
							string myImageFileName = Path.GetFileName(myImageFile);

							myWriter.WriteStartElement(MediaContentsConstants.cItemNodeName);

							myWriter.WriteAttributeString(MediaContentsConstants.cIdAttributeName, myImageFileName);

							myWriter.WriteStartElement(MediaContentsConstants.cImageNodeName);

							myWriter.WriteAttributeString(MediaContentsConstants.cFileAttributeName, myImageFileName);

							myWriter.WriteEndElement();

							myWriter.WriteEndElement();
						}
					}
				}

				myWriter.WriteEndElement();
			}
		}

		/// <summary>
		/// Creates files for every conceptual topic in order 
		/// to specify topic titles which are used to display 
		/// references to the topic.
		/// </summary>
		private void CreateCompanionFiles(string sandCastleConceptualTopicsCompanionFilesDirectory, ConceptualTopic[] conceptualTopics)
		{
			foreach (ConceptualTopic myTopic in conceptualTopics)
			{
				string myCmpFilePath = Path.Combine(
					sandCastleConceptualTopicsCompanionFilesDirectory, 
					string.Format(XmlCompanionFileConstants.cFileNameFormatString, myTopic.TopicId));

				using (StreamWriter myWriter = new StreamWriter(myCmpFilePath, false, Encoding.Default))
				{
					myWriter.Write(
						string.Format(XmlCompanionFileConstants.cContentsFormatString, myTopic.TopicId, myTopic.Title));
				}

				if (myTopic.ChildTopics != null && myTopic.ChildTopics.Count > 0)
					CreateCompanionFiles(sandCastleConceptualTopicsCompanionFilesDirectory, myTopic.ChildTopics.ToArray());
			}
		}

		/// <summary>
		/// Adds conceptual topics to the specified MSHelp2 TOC file.
		/// </summary>		
		private void AddConceptualTopicsToMsHelp2HxTFile(string tocFilePath, ConceptualTopic[] conceptualTopics)
		{
			#region Load the existing HxF file

			XmlDocument myDoc = new XmlDocument();
			myDoc.XmlResolver = null;
			myDoc.Load(tocFilePath);

			#endregion

			#region Get the root node

			XmlNode myRootNode = myDoc.SelectSingleNode(MsHelp2HxTFileConstants.cRootNodeName);

			#endregion

			AddConceptualTopicToMsHelp2HxTFileDoc(myDoc, myRootNode, conceptualTopics, true);

			#region Save the document

			myDoc.Save(tocFilePath);

			#endregion
		}

		/// <summary>
		/// Adds the specified conceptual topics to the specified xml document
		/// which represents MsHelp2 HxT (TOC) file.
		/// </summary>
		private void AddConceptualTopicToMsHelp2HxTFileDoc(XmlDocument doc, XmlNode parentNode, ConceptualTopic[] conceptualTopics, bool isInsertToBeginning)
		{
			XmlNode myNodeToInsertBefore;
			if (isInsertToBeginning)
				myNodeToInsertBefore = parentNode.FirstChild;
			else
				myNodeToInsertBefore = null;

			foreach (ConceptualTopic myTopic in conceptualTopics)
			{
				XmlElement myElem = doc.CreateElement(MsHelp2HxTFileConstants.cItemNodeName);

				if (string.IsNullOrEmpty(myTopic.XmlDescriptionFileName))
				{
					XmlAttribute myTitleAttr = doc.CreateAttribute(MsHelp2HxTFileConstants.cTitleAttributeName);
					myTitleAttr.Value = myTopic.Title;

					myElem.Attributes.Append(myTitleAttr);
				}
				else
				{
					XmlAttribute myUrlAttr = doc.CreateAttribute(MsHelp2HxTFileConstants.cUrlAttributeName);
					myUrlAttr.Value = Path.Combine(
						SandCastleOutputFilesConstants.cHtmlDirectoryRelativePath,
						myTopic.TopicId + SandCastleOutputFilesConstants.cHtmlFileExtension);

					myElem.Attributes.Append(myUrlAttr);
				}

				parentNode.InsertBefore(myElem, myNodeToInsertBefore);

				if (myTopic.ChildTopics != null && myTopic.ChildTopics.Count > 0)
					AddConceptualTopicToMsHelp2HxTFileDoc(doc, myElem, myTopic.ChildTopics.ToArray(), false);
			}
		}

		/// <summary>
		/// Views the HxS file produced by the BuildHelp method
		/// (defined in the IHelpBuilder interface).
		/// </summary>
		public void ViewResult(string outputDirPath, string name, string helpNamespace)
		{
			string myHelpFilePath = GetHelpFilePath(outputDirPath, name);
			if (File.Exists(myHelpFilePath))
			{
				string myHelpNamespace;
				if (string.IsNullOrEmpty(helpNamespace))
					myHelpNamespace = Path.GetFileNameWithoutExtension(myHelpFilePath);
				else
					myHelpNamespace = helpNamespace;

				RunProcess(
					null,
					null,
					HxRegFilePath,
					string.Format(
						"-r -n \"{0}\"",
						myHelpNamespace),
					null,
					true,
					false,
					false,
					false);

				RunProcess(
					null,
					null,
					HxRegFilePath,
					string.Format(
						"-n \"{0}\" -i \"{0}\" -s \"{1}\"",
						myHelpNamespace,
						myHelpFilePath),
					null,
					true,
					false,
					false,
					false);

				RunProcess(
					null,
					null,
					DExploreFilePath,
					string.Format(
						"/helpcol \"ms-help://{0}\"",
						myHelpNamespace),
					null,
					false,
					false,
					false,
					false);
			}
		}

		/// <summary>
		/// Determines whether the the HxS file produced by the BuildHelp method can be viewed
		/// (defined in the IHelpBuilder interface).
		/// </summary>
		public bool CanViewResult(string outputDirPath, string name)
		{
			string myHelpFilePath = GetHelpFilePath(outputDirPath, name);
			return File.Exists(myHelpFilePath);
		}

		/// <summary>
		/// Creates the conceptual topics metadata file
		/// (in fact, this is only for inserting common running header text
		/// into the result conceptual topic files)
		/// </summary>
		/// <param name="myMainBuildTempDirectory">The build directory</param>
		/// <param name="conceptualTopics">List of conceptual topics (top-level nodes)</param>
		private void CreateConceptualTopicsMetadataFile(string myMainBuildTempDirectory, ConceptualTopic[] conceptualTopics)
		{
			string myFileName = Path.Combine(myMainBuildTempDirectory, ConceptualTopicsMetadataFileConstants.cConceptualTopicsManifestName);
			using (XmlTextWriter myWriter = new XmlTextWriter(myFileName, Encoding.Default))
			{
				myWriter.Formatting = Formatting.Indented;
				myWriter.WriteStartDocument();
				myWriter.WriteStartElement(ConceptualTopicsMetadataFileConstants.cRootNodeName);

				AddConceptualTopicsToConceptualTopicsMetadataFile(myWriter, conceptualTopics);

				myWriter.WriteEndElement();
				myWriter.WriteEndDocument();
			}
		}

		/// <summary>
		/// Adds the specified list of topics and their sub-topics
		/// to the conceptual topics metadata file
		/// represented by the specified writer.
		/// </summary>
		public void AddConceptualTopicsToConceptualTopicsMetadataFile(XmlWriter writer, IList<ConceptualTopic> conceptualTopics)
		{
			if (conceptualTopics == null || conceptualTopics.Count == 0)
				throw new ArgumentException("SandCastleHelpBuilder.AddConceptualTopicsToConceptualTopicsMetadataFile(conceptualTopics)");

			if (writer == null)
				throw new ArgumentNullException("SandCastleHelpBuilder.AddConceptualTopicsToConceptualTopicsMetadataFile(writer)");

			for (int myTopicIndex = 0; myTopicIndex < conceptualTopics.Count; myTopicIndex++)
			{
				ConceptualTopic myTopic = conceptualTopics[myTopicIndex];

				writer.WriteStartElement(ConceptualTopicsMetadataFileConstants.cTopicNodeName);
				writer.WriteAttributeString(ConceptualTopicsMetadataFileConstants.cTopicIdAttributeName, myTopic.TopicId);
				writer.WriteRaw(ConceptualTopicsMetadataFileConstants.cRunningHeaderTextElementRawString);
				writer.WriteEndElement();

				if ((myTopic.ChildTopics != null) && (myTopic.ChildTopics.Count > 0))
					AddConceptualTopicsToConceptualTopicsMetadataFile(writer, myTopic.ChildTopics);
			}
		}

		/// <summary>
		/// Creates a manifest file for the specified conceptual topics.
		/// This manifest file is later used by AssemblyBuilder.
		/// </summary>
		/// <returns>The full path to the manisest file.</returns>
		private string CreateConceptualTopicManifestFile(ConceptualTopic[] conceptualTopics, string outputDirectory)
		{
			#region Check the arguments

			if (conceptualTopics == null || conceptualTopics.Length == 0)
				throw new ArgumentException("SandCastleHelpBuilder.CreateConceptualTopicManifestFile(conceptualTopics)");

			if (string.IsNullOrEmpty(outputDirectory))
				throw new ArgumentException("SandCastleHelpBuilder.CreateConceptualTopicManifestFile(outputDirectory)");

			#endregion

			string myOutFileName = ManifestXmlFileConstants.cConceptualTopicsManifestName;
			string myOutFilePath = Path.Combine(outputDirectory, myOutFileName);

			XmlTextWriter myWriter = new XmlTextWriter(myOutFilePath, Encoding.UTF8);
			try
			{
				myWriter.Formatting = Formatting.Indented;
				myWriter.WriteStartDocument();
				myWriter.WriteStartElement(ManifestXmlFileConstants.cRootNodeName);

				AddConceptualTopicsToManifestFile(myWriter, conceptualTopics);

				myWriter.WriteEndElement();
				myWriter.WriteEndDocument();
			}
			finally
			{
				myWriter.Flush();
				myWriter.Close();
			}

			return myOutFilePath;
		}

		/// <summary>
		/// Adds the specified list of topics and their sub-topics to the manifest file 
		/// represented by the specified writer.
		/// </summary>
		public void AddConceptualTopicsToManifestFile(XmlWriter writer, IList<ConceptualTopic> conceptualTopics)
		{
			if (conceptualTopics == null || conceptualTopics.Count == 0)
				throw new ArgumentException("SandCastleHelpBuilder.AddConceptualTopicsToManifestFile(conceptualTopics)");

			if (writer == null)
				throw new ArgumentNullException("SandCastleHelpBuilder.AddConceptualTopicsToManifestFile(writer)");

			for (int myTopicIndex = 0; myTopicIndex < conceptualTopics.Count; myTopicIndex++)
			{
				ConceptualTopic myTopic = conceptualTopics[myTopicIndex];

				writer.WriteStartElement(ManifestXmlFileConstants.cTopicNodeName);
				writer.WriteAttributeString(ManifestXmlFileConstants.cTopicIdAttributeName, myTopic.TopicId);

				if ((myTopic.ChildTopics != null) && (myTopic.ChildTopics.Count > 0))
					AddConceptualTopicsToManifestFile(writer, myTopic.ChildTopics);

				writer.WriteEndElement();
			}
		}

		/// <summary>
		/// Creates the output folder structure, copies necessary files to the output folders
		/// and adjust their contents before running the build process itself.
		/// </summary>
		private void PrepareOutputStuff(
			ProcessStepDelegate processStepCallback,
			string outputDirPath,
			string[,] assemblyFiles,
			ConceptualTopic[] conceptualTopics,
			string mediaDirectoryPath,
			string feedbackEmail, 
			string productName,
			string title,
			string company,
			string docVersion,
			string docSetList,
			string sandCastleConceptualTopicsCommentsDirectory,
			out string mainBuildTempDirectory,
			out string sandCastleOutputDirectory,
			out string sandCastleConfigFilePath,
			out string sandCastleConceptualTopicsConfigFilePath,
			out string sandCastleCommentsDirectory,
			out string sandCastleConceptualTopicsCompanionFilesDirectory,
			out string sandCastleSeedFilePath,
			out string sandCastleHtmlDirectory,
			out string msHelpTemplatesDirectoryPath,
			out string sandCastleMediaDirectoryPath)
		{
			#region Create output and temp directories

			if (!Directory.Exists(outputDirPath))
				Directory.CreateDirectory(outputDirPath);			

			mainBuildTempDirectory = Path.Combine(outputDirPath, SandCastleOutputFilesConstants.cMainBuildTempDirectoryName);
			if (Directory.Exists(mainBuildTempDirectory))
				Directory.Delete(mainBuildTempDirectory, true);

			Directory.CreateDirectory(mainBuildTempDirectory);

			sandCastleOutputDirectory = Path.Combine(
				mainBuildTempDirectory, SandCastleOutputFilesConstants.cSandCastleOutputDirectoryRelativePath);

			Directory.CreateDirectory(sandCastleOutputDirectory);

			string myExtractedFilesDirectory = Path.Combine(
				mainBuildTempDirectory,
				SandCastleSourceFilesConstants.cSandCastleExtractedFilesDirectoryRelativePath);

			Directory.CreateDirectory(myExtractedFilesDirectory);

			sandCastleConceptualTopicsCompanionFilesDirectory = Path.Combine(
				mainBuildTempDirectory,
				SandCastleSourceFilesConstants.cSandCastleXmlCompDirectoryRelativePath);

			Directory.CreateDirectory(sandCastleConceptualTopicsCompanionFilesDirectory);

			#endregion

			#region Create the HTML output folder

			sandCastleHtmlDirectory = Path.Combine(sandCastleOutputDirectory, SandCastleSourceFilesConstants.cHtmlDirectoryRelativePath);
			Directory.CreateDirectory(sandCastleHtmlDirectory);

			#endregion

			#region Copy MSHelp2 template files

			string myMsHelpTemplatesSrcDirectoryPath = Path.Combine(Application.StartupPath, MSHelp2BuildProcessConstants.cTemplatesRelativeDirectoryPath);
			msHelpTemplatesDirectoryPath = sandCastleOutputDirectory;
			CopyDirectory(myMsHelpTemplatesSrcDirectoryPath, msHelpTemplatesDirectoryPath, true);
			
			#endregion

			#region Copy xml descriptions

			sandCastleCommentsDirectory = Path.Combine(mainBuildTempDirectory, SandCastleSourceFilesConstants.cSandCastleCommentsDirectoryRelativePath);
			Directory.CreateDirectory(sandCastleCommentsDirectory);

			for (int myFileIndex = 0; myFileIndex < assemblyFiles.GetLength(0); myFileIndex++)
			{
				string myXmlFilePath = assemblyFiles[myFileIndex, 1];
				string myXmlFileName = Path.GetFileName(myXmlFilePath);

				File.Copy(
					myXmlFilePath,
					Path.Combine(sandCastleCommentsDirectory, myXmlFileName));
			}

			#endregion

			#region Copy the media files

			sandCastleMediaDirectoryPath = Path.Combine(
				mainBuildTempDirectory,
				SandCastleSourceFilesConstants.cSandCastleMediaDirectoryRelativePath);

			if (string.IsNullOrEmpty(mediaDirectoryPath))
				Directory.CreateDirectory(sandCastleMediaDirectoryPath); // we need this dir for SandCastle with our config file in any case
			else
				CopyDirectory(mediaDirectoryPath, sandCastleMediaDirectoryPath, true);
			
			#endregion

			#region Copy sandcastle templates

			string mySandCastleTemplatesSrcDirectoryPath = Path.Combine(Application.StartupPath, SandCastleSourceFilesConstants.cSandCastleTemplateRelativeDirectoryPath);
			CopyDirectory(mySandCastleTemplatesSrcDirectoryPath, mainBuildTempDirectory, true);
			sandCastleConceptualTopicsConfigFilePath = Path.Combine(mainBuildTempDirectory, SandCastleSourceFilesConstants.cConceptualTopicsSandCastleConfigFileName);
			sandCastleConfigFilePath = Path.Combine(mainBuildTempDirectory, SandCastleSourceFilesConstants.cMemberTopicsSandCastleConfigFileName);
			sandCastleSeedFilePath = Path.Combine(mainBuildTempDirectory, SandCastleSourceFilesConstants.cSeedHxFRelativePath);

			#endregion

			#region Adjust the configuration files

			AdjustSandCastleConfigFile(sandCastleConfigFilePath, sandCastleCommentsDirectory, docSetList);

			AdjustSandCastleConfigFile(sandCastleConceptualTopicsConfigFilePath, sandCastleConceptualTopicsCommentsDirectory, docSetList);

			#endregion			

			#region Adjust the feedback content file

			string myFeedbackContentFilePath = Path.Combine(mainBuildTempDirectory, SandCastleSourceFilesConstants.cFeedbackContentsRelativePath);
			AdjustSandCastleFeedbackContentFile(myFeedbackContentFilePath, feedbackEmail, productName, company, docVersion);

			#endregion

			#region Adjust our shared content file

			string mySharedContentFilePath = Path.Combine(mainBuildTempDirectory, SharedContentXmlFileConstants.cSharedContentXmlFileName);
			AdjustSharedContentFile(
				mySharedContentFilePath, title);

			#endregion
		}

		/// <summary>
		/// Adjusts the specified SandCastle configuration file in order to make 
		/// work with our DocMounter output file structure.
		/// </summary>
		private void AdjustSandCastleConfigFile(string sandCastleConfigFilePath, string commentsDirectoryPath, string docSetList)
		{
			XmlDocument myDoc = new XmlDocument();
			myDoc.Load(sandCastleConfigFilePath);

			#region Adjust the XML Comments Directory path

			XmlNode myNode = myDoc.SelectSingleNode(SandCastleConfigXmlFileConstants.cCommentsNodeXPath);
			myNode.Attributes[SandCastleConfigXmlFileConstants.cFilesAttributeName].Value =
				Path.Combine(commentsDirectoryPath, SandCastleConfigXmlFileConstants.cCommentsFilter);
			
			#endregion

			#region Adjust the MSHelp2 DocSet attribute section

			XmlNode myDocSetComponentNode = myDoc.SelectSingleNode(SandCastleConfigXmlFileConstants.cMSHelp2AttributesNodeXPath);
			if (myDocSetComponentNode != null)
			{
				if (string.IsNullOrEmpty(docSetList))
				{
					// Remove the corresponding component call from the .config file
					myDocSetComponentNode.ParentNode.RemoveChild(myDocSetComponentNode);
				}
				else
				{
					// Add the required DocSet attribute values to the .config file
					XmlNode myAttributesNode = myDoc.CreateNode(XmlNodeType.Element, SandCastleConfigXmlFileConstants.cAttributesNodeName, "");
					myDocSetComponentNode.AppendChild(myAttributesNode);
					string[] myDocSets = docSetList.Split(ParseInputDataConstants.cDocSetListDelimiter);
					foreach (string myDocSet in myDocSets)
					{
						XmlElement myElement = myDoc.CreateElement("", SandCastleConfigXmlFileConstants.cAttributeNodeName, "");

						XmlAttribute myNameAttribute = myDoc.CreateAttribute(SandCastleConfigXmlFileConstants.cNameAttributeName);
						myNameAttribute.Value = SandCastleConfigXmlFileConstants.cNameAttributeValue;
						myElement.Attributes.Append(myNameAttribute);

						XmlAttribute myValueAttribute = myDoc.CreateAttribute(SandCastleConfigXmlFileConstants.cValueAttributeName);
						myValueAttribute.Value = myDocSet;
						myElement.Attributes.Append(myValueAttribute);

						myAttributesNode.AppendChild(myElement);
					}
				}
			}
			#endregion

			myDoc.Save(sandCastleConfigFilePath);
		}

		/// <summary>
		/// Adjusts the running header text in the reference content file
		/// </summary>
		private void AdjustSharedContentFile(string sharedContentFilePath, string runningHeaderText)
		{
			XmlDocument myDoc = new XmlDocument();
			myDoc.Load(sharedContentFilePath);

			XmlNode myNode = myDoc.SelectSingleNode(SharedContentXmlFileConstants.cRunningHeaderTextNodeXPath);
			myNode.InnerText = runningHeaderText;

			myDoc.Save(sharedContentFilePath);
		}

		/// <summary>
		/// Adjusts the specified feedback content file in order to fill up the 
		/// feedback link information.
		/// </summary>
		private void AdjustSandCastleFeedbackContentFile(string feedbackContentFilePath, string feedbackEmail, string productName, string company, string docVersion)
		{
			XmlDocument myDoc = new XmlDocument();
			myDoc.Load(feedbackContentFilePath);

			XmlNode myNode = myDoc.SelectSingleNode(SandCastleFeedbackContentsXmlFileConstants.cEmailNodeXPath);
			myNode.InnerText = feedbackEmail;

			myNode = myDoc.SelectSingleNode(SandCastleFeedbackContentsXmlFileConstants.cProductNameNodeXPath);
			myNode.InnerText = productName;

			myNode = myDoc.SelectSingleNode(SandCastleFeedbackContentsXmlFileConstants.cCompanyNodeXPath);
			myNode.InnerText = company;

			myNode = myDoc.SelectSingleNode(SandCastleFeedbackContentsXmlFileConstants.cDocVersionNodeXPath);
			myNode.InnerText = docVersion;

			myDoc.Save(feedbackContentFilePath);
		}

		/// <summary>
		/// Fills up the MSHelp2 HxF file with the list of all the help files.
		/// </summary>
		private void FillMsHelp2HxFFile(
			string sandCastleSeedFilePath, 
			string sandCastleHtmlDirecotry,
			string sandCastleOutputDirectory,
			string sandCastleMediaDirectory,
			string filePath)
		{
			#region Load the HxF template

			XmlDocument myDoc = new XmlDocument();
			myDoc.XmlResolver = null;
			myDoc.Load(filePath); 

			#endregion

			#region Get the root node

			XmlNode myFileListNode = myDoc.SelectSingleNode(MSHelp2HxFFileConstants.cFileListNodeXPath); 
			
			#endregion

			#region Add the presentation style files (by using the Seed file)

			#region Open the Seed file

			StreamReader myTextReader;
			string myLine;
			myTextReader = File.OpenText(sandCastleSeedFilePath); 

			#endregion

			myLine = myTextReader.ReadLine();
			while (myLine != null)
			{
				#region Read the file locations

				// An example of a line from the 'seed' file:
				// Scripts/*.js

				string[] mySubFolderInfo = myLine.Split('/');
				string mySubFolder = mySubFolderInfo[0];
				DirectoryInfo myDirInfo = new DirectoryInfo(Path.Combine(sandCastleOutputDirectory, mySubFolder)); 

				FileInfo[] myFiles = myDirInfo.GetFiles(mySubFolderInfo[1]);

				#endregion

				foreach (FileInfo myFile in myFiles)
				{
					AddFileReferenceToMsHelp2HxFFileDoc(
						Path.Combine(mySubFolder, myFile.Name), 
						myDoc, 
						myFileListNode);
				}

				myLine = myTextReader.ReadLine();
			}
			myTextReader.Close(); 

			#endregion

			#region Add the HTML files

			DirectoryInfo myHtmlFilesDirInfo = new DirectoryInfo(sandCastleHtmlDirecotry);
			FileInfo[] myHtmlFiles = myHtmlFilesDirInfo.GetFiles();
			foreach (FileInfo myHtmlFile in myHtmlFiles)
			{
				AddFileReferenceToMsHelp2HxFFileDoc(
					Path.Combine(myHtmlFilesDirInfo.Name, myHtmlFile.Name), myDoc, myFileListNode);
			}
			
			#endregion

			#region Add the media files
			
			DirectoryInfo myMediaFilesDirInfo = new DirectoryInfo(sandCastleMediaDirectory);
			FileInfo[] myMediaFiles = myMediaFilesDirInfo.GetFiles();
			foreach (FileInfo myMediaFile in myMediaFiles)
			{
				AddFileReferenceToMsHelp2HxFFileDoc(
					Path.Combine(myMediaFilesDirInfo.Name, myMediaFile.Name), myDoc, myFileListNode);
			}

			#endregion

			myDoc.Save(filePath);
		}

		/// <summary>
		/// Adds the specified file reference to the specified xml document
		/// which represents MsHelp2 HxF file.
		/// </summary>
		private void AddFileReferenceToMsHelp2HxFFileDoc(string filePath, XmlDocument doc, XmlNode fileListNode)
		{
			XmlElement myElem = doc.CreateElement(MSHelp2HxFFileConstants.cFileNodeName);
			XmlAttribute myAttr = doc.CreateAttribute(MSHelp2HxFFileConstants.cUrlAttributeName);
			myAttr.Value = filePath;
			myElem.Attributes.Append(myAttr);
			fileListNode.AppendChild(myElem);
		}

		[System.Runtime.InteropServices.DllImport("user32.dll")]
		private static extern
			bool SetForegroundWindow(IntPtr hWnd);

		/// <summary>
		/// Runs the specified process and reads its output.
		/// </summary>
		private void RunProcess(
			ProcessStepDelegate processStepCallback, 
			Func<bool> shouldStopCallback,
			string fileName, 
			string arguments, 
			string workingDirectory, 
			bool isWaitForExit,
			bool isRedirectOutput,
			bool canStop,
			bool forceBringToFront)
		{
			#region Check build is stopped

			if (canStop)
			{
				if (shouldStopCallback != null && shouldStopCallback())
					throw new BuildStoppedException();
			}

			#endregion

			string myOldCurDir = Environment.CurrentDirectory;
			if (!string.IsNullOrEmpty(workingDirectory))				
				Environment.CurrentDirectory = workingDirectory;

			try
			{
				if (processStepCallback != null)
					processStepCallback(
						string.Format("{0} {1}", Path.GetFileName(fileName), arguments ?? string.Empty),
						false);

				Process myProcess = new Process();

				myProcess.StartInfo.FileName = fileName;
				myProcess.StartInfo.Arguments = arguments;
				myProcess.StartInfo.UseShellExecute = false;
				myProcess.StartInfo.RedirectStandardOutput = isRedirectOutput;
				myProcess.StartInfo.CreateNoWindow = true;

				if (!string.IsNullOrEmpty(workingDirectory))
					myProcess.StartInfo.WorkingDirectory = workingDirectory;

				myProcess.Start();

				if (forceBringToFront)
				{
					//TODO: Decide: Make 'SetForegroundWindow timeout' an adjustable DocMounter option if it works in Vista/7 (now only in WinXP)
					myProcess.WaitForInputIdle();
					System.Threading.Thread.Sleep(750);
					SetForegroundWindow(myProcess.MainWindowHandle);
				}

				if (isRedirectOutput)
				{
					StreamReader myStreamReader = myProcess.StandardOutput;
					while (!myStreamReader.EndOfStream)
					{
						string myString = myStreamReader.ReadLine();
						if (processStepCallback != null)
							processStepCallback(myString, true);

						#region Check build is stopped

						if (canStop)
						{
							if (shouldStopCallback != null && shouldStopCallback())
							{
								myProcess.Kill();
								throw new BuildStoppedException();
							}
						}

						#endregion
					}
				}
			
				if (isWaitForExit)
				{
					while (!myProcess.HasExited)
					{
						#region Check build is stopped

						if (canStop)
						{
							if (shouldStopCallback != null && shouldStopCallback())
							{
								myProcess.Kill();
								throw new BuildStoppedException();
							}
						}

						#endregion
					}

					if (myProcess.ExitCode != 0)
						throw new Exception(string.Format("{0} error", Path.GetFileName(fileName)));
				}
			}
			finally
			{
				Environment.CurrentDirectory = myOldCurDir;
			}
		}

		/// <summary>
		/// Copies the specified directory and its sub-directories.
		/// </summary>
		private void CopyDirectory(string src, string dst, bool recursive)
		{
			String[] myFiles;

			if (dst[dst.Length - 1] != Path.DirectorySeparatorChar)
				dst += Path.DirectorySeparatorChar;

			if (!Directory.Exists(dst)) Directory.CreateDirectory(dst);
				myFiles = Directory.GetFileSystemEntries(src);

			foreach (string Element in myFiles)
			{
				if (Directory.Exists(Element))
				{
					if (recursive)
						CopyDirectory(Element, dst + Path.GetFileName(Element), recursive);
				}
				else
					File.Copy(Element, dst + Path.GetFileName(Element), true);
			}
		}

		/// <summary>
		/// Returns the full path to the compiled help file.
		/// </summary>
		private string GetHelpFilePath(string outputDirPath, string helpName)
		{
			if (String.IsNullOrWhiteSpace(outputDirPath))
				return null;
			return Path.Combine(outputDirPath, helpName + MSHelp2BuildProcessConstants.cHelpSolutionExtension);
		}

		#endregion

		#region Properties

		/// <summary>
		/// Stores the path to the Sandcastle tool
		/// </summary>
		public string SandcastleToolPath
		{
			get
			{
				return System.Environment.GetEnvironmentVariable(ExternalToolsConstants.cSandCastleToolSystemVar, EnvironmentVariableTarget.Machine);
			}
		}

		/// <summary>
		/// Stores the path to the hxcomp.exe/hxreg.exe tools
		/// (defined in the IHelpBuilder interface).
		/// </summary>
		public string GlobalHxToolsPath
		{
			get;
			set;
		}

		/// <summary>
		/// Gets the full path to the HxReg utility.
		/// </summary>
		private string HxRegFilePath
		{
			get
			{
				return Path.Combine(GlobalHxToolsPath, ExternalToolsConstants.cHxRegExeFileName);
			}
		}

		/// <summary>
		/// Stores the path to the DExplore.exe application
		/// (defined in the IHelpBuilder interface).
		/// </summary>
		public string GlobalDExplorePath
		{
			get;
			set;
		}

		/// <summary>
		/// Gets the full path to the dexplore help viewer.
		/// </summary>
		private string DExploreFilePath
		{
			get
			{
				return Path.Combine(GlobalDExplorePath, ExternalToolsConstants.cDExploreExeFileName);
			}
		}

		/// <summary>
		/// Stores a boolean value which indicates whether to save temporary production files after build
		/// (defined in the IHelpBuilder interface).
		/// </summary>
		public bool GlobalDeleteIntermediateFiles
		{
			get;
			set;
		}

		#endregion				
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior)
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Written By
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.
This is a Organisation

2 members

Comments and Discussions