Custom CHM index file (hhk) for VS2005 style

Jul 22, 2010 at 1:51 PM

For a few years I have been successfully building CHM documentation using the Prototype style with some customizations including my custom ReflectionToChmIndex.xsl transformation. Why: CHM indexes generated by default can be slightly improved. In particular I managed to avoid annoying entries with same names (e.g. methods Add in several classes) that are difficult to resolve for a user of CHM - a help viewer only shows silly modal dialogs with several absolutely identical entries to choose from.

I decided to switch to VS2005 style. Basically everything is fine. But my custom ReflectionToChmIndex.xsl apparently is not called in this scenario and the result CHM indexes have mentioned annoying flaws.

I have found why it happens in the script scbuild.ps1: ReflectionToChmIndex.xsl is used there only for the Prototype style:

----- 8< -----
function CreateChmProject {
if ($Style -eq "prototype") {
&$XslTransform $TempDir\ReflectionData\targets.xml `
/xsl:$DxRoot\ProductionTransforms\ReflectionToChmIndex.xsl `
----- 8< -----

Some questions:
1) Is it OK if I use the same way for VS2005? (I changed scbuild.ps1 and it looks working fine for VS2005, too, but this way is hacky, indeed).
2) If it is OK, can this option be provided by the script in the version next?
3) Is there some official way to customize CHM indexes during build of VS2005 style docs now?


Jul 22, 2010 at 4:42 PM

The ChmBuilder.exe tool is responsible for generating the Help 1 index file.  I'm not sure why the transformation is used for the Prototype style.  The Prototype style has been deprecated so it could be that the code to run the transformation is just left over and wasn't removed when they updated the script to use the new tool.  If it works for you, you can leave it in so that it overrides the ChmBuilder.exe output.



Jul 22, 2010 at 6:06 PM


Thank you for your replay. So it looks like the new tool makes customization of indexes impossible. It would be nice if it or the infrastructure provides some options on how indexes should look like. So far I am aware of the only index defect – multiple indexes with same names. I solved the problem by adding class/struct names to member names: that is, instead of index items “Add method” I tell to make “Add method, Class1”, “Add method, Class2”, and etc. But this is only possible now with use of a transformation file (obsolete way).

If ChmBuilder.exe could do that itself (optionally, indeed) that would be very convenient. For example this can be configurable via reference_content.xml (or something), for example:

How it is done for “locationInformation”:
 <item id="locationInformation">Assembly: {0} (Module: {1})</item>

How it can be done for indexes:

Default (just like it is now):
 <item id="chmIndex">{0} {1}</item>
, where {0} is for a member name, {1} is for a member kind (method/property/field)

Customized (with some more parameters {i}, {j}, .. documented):
 <item id="chmIndex">{0} {1}, {2}.{3}</item>
, where, say, {2} is for a namespace, {3} is for a type name.

Having this information available, ChmBuilder.exe can produce indexes in a way specified by a user.


Jul 22, 2010 at 6:27 PM

ChmBuilder uses the index metadata from the topics to generate the index file.  They are written to the MSHelp:Keyword attributes (Index="K") by the metadataHelp20.xsl Sandcastle transformation.  As such, you may be able to modify that transformation file so that it outputs the value that you want.



Jul 22, 2010 at 7:18 PM

It’s good news, thank you. I’ll try (though this file so far looks very cryptic to me, I am not an expert in xsl in any way).


BTW, I have just found that what I proposed (rather naively) is almost there, in reference_content.xml we have (for example for methods):

<item id="methodIndexEntry">{0} method</item>

I tried to change it, say, adding XYZ:

<item id="methodIndexEntry">{0} method XYZ</item>

and this worked, indexes have got "XYZ" added.

I also tried {1} (who knows, maybe?):

<item id="methodIndexEntry">{0} method, {1}</item>

and it failed to build...

Jul 22, 2010 at 7:47 PM

As for my hack with use of ReflectionToChmIndex.xsl for VS2005, the outcome hhk file is different in a few ways (not only my customizations), so that it is not quite good to be used as a workaround.

Jul 22, 2010 at 8:24 PM

Adding a parameter to the resource item requires that the parameter value be added in the XSL transformation too.  If you can find the place where it uses the methodIndexEntry item, you could possibly add the extra parameter.  It's most likely one of the item specific sections of the transformation so you won't find the name exactly, just the "IndexEntry" suffix being concatenated on the end of a variable.



Jul 23, 2010 at 11:05 AM

Thank you for your help, I really appreciate it.

I tried and gave up after a while; it goes too far for my goals. I’ll stay with one of my previous approaches.

I hope Sandcastle will provide some kind of CHM index customization in the future versions. Or even better it will not produce CHM indexes with same names because CHM viewer is simply not well designed for this scenario.

Jul 23, 2010 at 5:07 PM

For those who are interested: in my custom ReflectionToChmIndex.xsl I replaced these lines:

<xsl:template match="api[apidata/@group='member' and topicdata/@notopic !='']" >
<xsl:variable name="type" select="key('index',containers/type/@api)" />
<xsl:if test="not(apidata/@subgroup='constructor')">
<xsl:call-template name="createIndexEntry">
<xsl:with-param name="text">
<xsl:value-of select="concat(apidata/@name,' ',apidata/@subgroup)" />


<xsl:template match="api[apidata/@group='member' and not(topicdata/@notopic)]" >
<xsl:variable name="type" select="key('index',containers/type/@api)" />
<xsl:if test="not(apidata/@subgroup='constructor')">
<xsl:call-template name="createIndexEntry">
<xsl:with-param name="text">
<xsl:value-of select="concat(apidata/@name,' ',apidata/@subgroup,', ',substring($type/@id,3))" />

As a result, I got member indexes included (in the original version members are not included, a bug, perhaps, it's unlikely by design) and in most cases there are no indexes with same names. Namely, I have "SomeCommonName method, Namespace.Class" with different Namespace.Class instead of multiple "SomeCommonName method" entries.

Jul 26, 2010 at 6:18 PM

I was able to fix the problem with creating unique names for the index by changing the following section within the topicTitlePlain template in utilities_reference.xls (line 522) from

<xsl:call-template name="shortNamePlain">
  <xsl:with-param name="qualifyMembers" select="$qualifyMembers" />


<xsl:call-template name="shortNamePlain">
  <xsl:with-param name="qualifyMembers" select="true()" /> <!-- MODIFIED: Support more meaningful names in 'topics found' dialog. -->

Jul 27, 2010 at 11:20 AM


I tried your solution and it worked just fine. I like it. Though it still takes more clicks to get to the target in cases of duplicated names, the solution is simpler and it preserves index structure produced by ChmBuilder.exe which is better for VS2005 style than produced by ReflectionToChmIndex.xsl.