使用 Sandcastle Help File Builder 为 .NET 组件创建文档
本文展示了如何在代码中创建文档,编写附加主题以及使用 Sandcastle Help File Builder 编译帮助文件。
目录
引言
良好的文档是一个成功产品的重要组成部分。创建对软件产品或组件的功能和能力进行全面详尽的描述需要时间和耐心。在本文中,我将讨论为 .NET 组件创建文档的一些实际方面。
假设我们已经完成或接近完成 .NET 开发人员库的创建(此处开发人员是最终用户)。库的 API 是完美的,错误数量少得令人印象深刻,实际上它不是一个库,而是一个完美代码的宝库。只剩下最后一件事要做。我们需要向用户解释如何使用这个很棒的产品。
编写文档有不同的方法。有些团队倾向于在产品构思之初就开始编写文档。另一些团队则将编写手册推迟到稍后。在某些团队中,有些人会走访开发人员和经理,询问问题并积累关于产品的知识。这些人负责为产品编写文档。在许多小型团队中,没有这样的人,文档通常由产品开发人员编写。一些团队使用第三方工具,如 Help & Manual,它作为一个功能齐全的文本处理器,可以用来创建具有非常复杂页面布局的文档。这类工具也适合生成各种格式的文档,如 PDF、CHM 等。许多人采用了近年来广泛提倡的另一种方法——直接在产品代码中编写文档。
我使用过第三方工具,也直接在代码中编写文档,尝试过在开发之前或之后开始编写文档。最终,我个人认为最好将编写手册推迟到产品开发周期的后半段。越接近完成,API、功能集等就越稳定,对文档的调整也就越少。事实证明,直接在代码中编写文档比使用第三方工具更方便,尽管起初看起来恰恰相反。本文就是关于如何直接在代码中编写文档以及之后可以对编写的内容做什么。
文档化 API
C# 和 VB.NET 编译器能够识别以特殊方式(XML 注释)装饰的注释,并在需要时创建 XML 文件,然后可以使用该文件生成文档。要以这种方式创建文档,需要使用 XML 注释描述所有 public
代码实体(类、接口、方法等)。XML 注释以三个正斜杠开头。它看起来像这样
/// <summary>
/// Gets the R component from ABGR value returned by
/// <see cref="O:BitMiracle.LibTiff.Classic.Tiff.ReadRGBAImage">ReadRGBAImage</see>.
/// </summary>
/// <param name="abgr">The ABGR value.</param>
/// <returns>The R component from ABGR value.</returns>
public static int GetR(int abgr)
{
return (abgr & 0xff);
}
默认情况下,从注释创建 XML 文件是禁用的。它应该在项目属性的“生成”选项卡中启用。

结果,在每次生成可执行文件或程序集时都会创建一个 XML 文件。该文件将包含代码中的所有 XML 注释,包括所有非 public 实体的注释。该文件本身很有用,因为当您将其与程序集放在一起时,Visual Studio 中的 IntelliSense 功能将使用该文件中的信息来显示程序集的方法、属性和参数的描述。上面显示的 GetR
函数为例,这是它的样子

然而,在大多数情况下,生成的 XML 文件将包含用户不应看到的非 public 实体的注释。在本文后面,我将展示如何自动从 XML 文件中删除非 public 实体的信息。
我不会描述所有可能的 XML 注释标签,但会尝试简要介绍最常用的标签。
summary
标签用于描述类、接口、枚举、类或接口的方法和属性,以及枚举的成员。param
标签用于描述方法的参数。此标签应用于每个方法参数。returns
标签用于描述方法的返回值(如果有)。value
标签对于描述属性接受或返回的值很有用。从某种意义上说,value
标签是 returns
标签的模拟,但它用于属性而不是方法。
/// <summary>
/// Gets the font ascent.
/// </summary>
/// <value>The font ascent.</value>
/// <remarks>Ascent is the maximum height above the baseline reached
/// by glyphs in this font, excluding the height of glyphs for
/// accented characters.</remarks>
public short Ascent
{
get
{
return Impl.Ascent;
}
}
非常有用但遗憾的是经常被忽略的是 remarks
标签,它允许您为代码实体指定注释。此标签可用于几乎任何代码实体(枚举值除外)的注释。实际上,您可以为 enum
值使用 remarks 标签,但使用默认样式(vs2005 样式)通过 XML 注释生成的已编译文档(CHM 文件)将不包含此类注释。这种默认行为显然降低了 remarks 对 enum
值的实用性。
这里还有一些实用的观察和建议。
我建议您下载并安装 Visual Studio 的 GhostDoc 插件。此插件与所有高于 Visual Studio 2005 的版本兼容,并极大地简化了 XML 注释的编写。当您按下 Ctrl-Shift-D 时,GhostDoc 会在光标位置附近生成并插入方法的 XML 注释或属性。该插件会插入所有必需的标签,并根据类型、名称和其他上下文信息为它们生成文本。通常,您只需要更正和补充生成的文本。
直接在代码中编写文档的最大缺点是注释有时比代码本身占用的空间更多。这可能会导致代码难以阅读。为了规避这个问题,将 public
接口与其实现完全分离非常方便。
已编译的文档将为每组重载方法提供一个单独的页面。您可以在 Docotic.Pdf 库帮助 中查看这样的页面。对于要显示在这些页面“重载列表”部分之上的文本,应使用 overloads
标签。
/// <summary>
/// Adds the new image with the data and properties of the specified
/// <see cref="Image"/> to the end of the collection of document images.
/// </summary>
/// <param name="image">The existing <see cref="Image"/> from which
/// to create the <see cref="PdfImage"/>.</param>
/// <returns>The newly added <see cref="PdfImage"/> for the first
/// frame (page) found in the existing <see cref="Image"/>.</returns>
/// <overloads>Adds the new image to the end of the collection of
/// document images.</overloads>
/// <remarks>This method adds a new image for each frame (page) found
/// in the existing <see cref="Image"/>.</remarks>
public PdfImage AddImage(Image image)
{
return impl.AddImage(image);
}
您可能希望在 XML 注释中提供指向另一个方法或类型的链接。对于此类链接,您应该使用类似的文本
<see cref="X:MEMBER">link text</see>
上面的代码中,X
是一个可选的前缀,表示实体类型,MEMBER
是实体的完整或部分规范。类型前缀如下:T
表示类,M
表示方法,P
表示属性,O
表示重载方法组。您可以 Omit 前缀并使用部分规范,用于同一类中两个方法之间的链接,或同一命名空间中的两个实体之间的链接。
以下代码显示了在 PdfFont
类中属性的描述中,使用部分规范链接到 PdfFontEmbedStyle enum
。enum
和类都包含在同一个命名空间中
public sealed class PdfFont
{
...
/// <summary>
/// Gets or sets the <see cref="PdfFontEmbedStyle"/> value that specifies
/// how this font is embedded into the document.
/// </summary>
/// <value>The <see cref="PdfFontEmbedStyle"/> value that specifies
/// how this font is embedded into the document.</value>
public PdfFontEmbedStyle EmbedStyle
{
get
{
return Impl.EmbedStyle;
}
set
{
Impl.EmbedStyle = value;
}
}
}
如果您链接到另一个命名空间中的实体、重载方法组或重载方法组中的特定方法,您应该始终使用完整规范,否则您的链接在已编译的文档中可能会中断。具有完整规范的链接示例
- 链接到属性
<see cref="P:System.Exception.HResult"/>
- 链接到方法
<see cref="M:BitMiracle.LibTiff.Classic.Tiff.GetR(System.Int32)"/>
- 链接到重载方法组
<see cref="O:BitMiracle.LibTiff.Classic.Tiff.PrintDirectory"/>
- 链接到类
<see cref="T:BitMiracle.LibTiff.Classic.TiffTagMethods"/>
正如您所见,任何完整规范都包含方法参数。这有助于解析链接,但会使链接文本复杂化。您可以通过复制先前生成的 XML 注释文件中的完整规范来节省手动工作。
重载方法组的链接存在一个令人头疼的问题。Visual Studio 要求此类引用被指定为 O:XXX.YYY
,而 Sandcastle Help File Builder(我稍后将使用此工具来编译帮助文件)要求此类引用被指定为 Overload:XXX.YYY
。为了解决这个问题,我使用了一个简单的脚本,该脚本在 Post-build 事件中调用,并将已生成 XML 注释文件中的所有 O:
出现替换为 Overload:
。
对于链接到 API 描述不相关的外部页面或 Internet 上的资源,请使用传统的 <a>
标签和 href 属性。例如,<a href = "54cbd23d-dc55-44b9-921f-3a06efc2f6ce.htm">链接文本</a>
或 <a href = "http://site.com/page.html">另一个链接文本</a>
。在第一个示例中,href
属性包含格式为“TOPIC_ID.htm”的字符串。TOPIC_ID
是什么以及在哪里可以获得它将在后面介绍。
您可以阅读以下文章了解有关 XML 注释的更多信息
编译帮助文件
一旦您的组件的 XML 注释准备就绪,您就可以从中生成文档文件。我更喜欢为此任务使用 Sandcastle 和 Sandcastle Help File Builder (SHFB)。有些人更喜欢 DocProject。您可能想阅读 Stack Overflow 上关于 SHFB 和 DocProject 的 讨论。如果您决定使用 SHFB,那么您应该
- 下载并安装 Sandcastle。
- 下载并安装 Sandcastle Help File Builder。
- 下载并应用 Sandcastle Styles 补丁。
- 如果您在构建 HTML 帮助格式文档时遇到任何问题,您需要验证 itircl.dll 是否存在并已在您的 Windows 副本中注册。通常可以在 System32 文件夹中找到此 DLL,并且必须使用 regsvr32 进行注册。您可以在 Rick Stone 的 Tips 'n Tricks 中了解更多关于此信息。
让我们以 CHM 格式构建文档。为此,请运行 Sandcastle Help File Builder 并配置项目属性。您可以使用“ComponentConfigurations”属性配置 SHFB 在构建过程中使用的附加组件。如果您不知道可能需要哪些组件,可以选择所有组件。无论如何,我建议您始终使用 IntelliSense Component,因为它会自动创建输入 XML 文件的副本,并清理掉所有非 public 代码实体的注释。您应该为用户提供 IntelliSense Component 生成的 XML 文件,而不是在构建组件时创建的 XML 文件。

此外,我建议更改以下属性
- 在Build 部分:FrameworkVersion
- 在Help File 部分:CopyrightHref, CopyrightText, FeedbackEMailAddress, FeedbackEMailLinkText, HelpTitle, HtmlHelpName
- 在Paths 部分:OutputPath
接下来,在“项目资源管理器”窗口中指定“文档源”。我建议您指定组件构建期间生成的 DLL 和 XML 文件作为文档源,而不是您的项目文件。如果您将项目文件指定为文档源,可能会遇到一个问题:XML 注释中的更改并非总是反映在文档中。即使在重新构建文档之后。我不知道这个问题该归咎于谁。
另一个重要步骤是描述 SHFB 中的命名空间。您的代码不能有命名空间的 XML 注释,因此您需要手动在 SHFB 中指定此类注释。应为此任务使用“Comments”部分和“NamespaceSummaries”属性。您可以使用标准的 HTML 标签来注释命名空间。
文档项目已设置好,现在可以构建 CHM 文件了。我们应该执行“Documentation->Build Project”命令,如果一切操作正确,我们将获得一个漂亮的 MSDN 风格的帮助文件。
有关 Sandcastle Help File Builder 的更多信息,请参阅 本文。
编写附加主题
类、方法和其他代码实体的描述是文档的重要组成部分,但还不足够。好的文档通常包含附加的文章、示例、FAQ 等。让我们看看如何向帮助文件中添加主题。
首先,您需要打开“Content Layout”窗口。为此,请在“Project Explorer”窗口中右键单击,然后在上下文菜单中选择“Add->New Item->Content Layout”。应通过“Content Layout”窗口向文档添加主题。您还可以指定主题的顺序、默认主题等。

主题的标记语言是 MAML。它是一种基于 XML 的格式。主题存储在 *.aml 文件中。Sandcastle Help File Builder 包含一组相当方便的主题模板。对我来说,最有用的模板是 **Conceptual** 和 **Walkthrough**。
每个新主题都会获得一个主题 ID。稍后,此 ID 将用于生成从主题标记生成的 HTML 文件的名称。生成的 HTML 文件将包含在已编译的帮助文件中。主题 ID 也用于从其他主题或组件代码中的 XML 注释中引用主题。
创建新主题时,SHFB 会提示您将主题保存到文件中。文件的默认名称是“TOPIC_ID.aml”。您可以并且可能应该将默认名称更改为类似“Topic Title.aml”的内容。
让我们看看 SHFB 中一些在编辑主题时很有用的 GUI 元素。

![]() |
设置默认主题。打开 CHM 文件时将打开此主题。 |
![]() |
设置 API 插入点。根据所选选项,从代码中的 XML 注释生成的页面将插入在标记为 API 插入点的元素之前、之后或作为其子元素。 |
![]() |
打开当前主题的预览。 |
![]() |
插入指向主题的链接模板。您应该用目标主题的主题 ID 填充模板。 |
![]() |
插入 MAML 标签。您可以从下拉列表中选择要插入的标签。 |
您可以使用“Entity Reference”窗口(在上图右侧显示)将代码实体引用插入到当前打开的主题中。但我认为这个窗口不是很方便,因为要在光标位置插入链接,我们需要先在编辑器中打开主题,然后打开“Entity Reference”窗口,然后编写实体名称的一部分或全部,然后在搜索结果中找到实体,然后双击它。我更喜欢手动插入引用。为此,我在上一个生成日志中找到引用的文本。
如果您需要将代码片段插入到主题中,应该使用 <code>
标签。例如
<code language="cs">
private void helloWorld()
{
Console.WriteLine("Hello World!");
}
</code>
要将图像插入到主题中,您应该执行以下操作
- 在“Project Explorer”窗口中右键单击。然后选择上下文菜单中的“Add->Existing Item”。系统会提示您选择图像。
- 在“Project Explorer”窗口中选择图像。然后将“BuildActionAction”属性值更改为“Image”,将“ImageId”属性值更改为您选择的 ID。此 ID 将用于链接到此图像。
要将图像插入主题,您应该像这样使用 mediaLink
标签
<mediaLink><image xlink:href="ImageId" placement="center" /></mediaLink>
不幸的是,SHFB 中当前内置编辑器的功能还不完善。例如,标签不会自动关闭,许多操作没有关联的热键,并且一些标准的 MAML 标签无法通过工具栏插入。奇怪的是,我发现大多数主题创作任务使用 Visual Studio XML 编辑器更容易执行。当然,您可以在编写主题时使用任何其他 XML 编辑器。
以上是我关于如何执行基本主题创作任务的描述。如果您想了解更多信息,我推荐以下链接
集成到构建过程
您可以将 Sandcastle Help File Builder 项目文件(*.shfbproj)包含到 Visual Studio 解决方案中,但它不会作为一个完整的项目添加。也就是说,该项目只会添加到“Solution Items”组中,您将无法看到其内容。
要将 SHFB 项目包含到 Visual Studio 解决方案中,请执行以下操作
- 在解决方案根目录上右键单击,然后在上下文菜单中选择“Add->Existing Item…”,然后选择一个 SHFB 项目。该项目将添加到“Solution Items”组中。
- 在“Solution Items”组中右键单击 SHFB 项目名称,然后在上下文菜单中选择“Open With…”,然后单击“Add”以添加新程序到列表中。输入 SandcastleBuilderGUI.exe 的路径,并为友好名称输入“Sandcastle Help File Builder GUI”。单击 OK 保存,然后单击“Set as Default”按钮。
之后,您将能够通过双击帮助项目名称从 Visual Studio 中打开帮助项目。
我认为从命令行构建帮助文件的能力更有用。例如,您可以在 Post-Build 事件上构建帮助文件。您可以使用以下命令从命令行构建 Sandcastle Help File Builder 项目
%SystemRoot%\Microsoft.NET\Framework\v3.5\MSBuild.exe"
/p:Configuration=Release Help.shfbproj
上面的行中,Help.shfbproj 是要构建的 SHFB 项目的文件名。
希望本文能帮助您开始为您的项目编写文档。祝您的帮助文件创作顺利!
附注:如果您想查看 SHFB 项目和主题文件的示例,可以随时下载 LibTiff.Net 的源代码包。