An Exploration Into Launching Context-Sensitive HTML Help with Topic IDs in VB.NET

I spent this evening investigating the HTML Help API as implemented in .NET. At work, I'm implementing HTML Help in a .NET 1.1 application.

It kept bugging me that I couldn't get any of the Help.ShowHelp overloads to support context-sensitive Help that uses integers as context/topic IDs. I'm referring to HTML Help projects that have a [Map] section with declarations such as

[MAP]
#define HID_FIVE 5
#define HID_TEN 10

and an [ALIAS] section that turns the mapped values into topic names like these

[ALIAS]
HID_FIVE=Topic_5.htm
HID_TEN=Topic_10.htm

When you use the HTML Help API in VB.NET, you can pass the Int32 value to the HTML Help API (HtmlHelpA()) like this to launch a specific topic:

    Private Declare Function HTMLHelp_ContextId _
    Lib "hhctrl.ocx" Alias "HtmlHelpA" _
    (ByVal hWnd As IntPtr, _
    ByVal lpHelpFile As String, _
    ByVal wCommand As Int32, _
    ByVal dwData As Int32) As Int32

    Private Sub Button3_Click _
    (ByVal sender As System.Object, _
     ByVal e As System.EventArgs) _
    Handles Button3.Click
        Dim RetVal As Int32
        Const HH_HELP_CONTEXT = &HF
        RetVal = HTMLHelp_ContextId _
        (IntPtr.Zero, "CHM_Checker_Help.chm", _
         HH_HELP_CONTEXT, 5)
    End Sub

Likewise, if you use Interop, you can launch a specific topic like this:

    <System.Runtime.InteropServices.DllImport _
    ("hhctrl.ocx", CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _
     Public Shared Function HtmlHelp _
     (ByVal hwndCaller As System.Runtime.InteropServices.HandleRef, _
     <System.Runtime.InteropServices.MarshalAs _
     (System.Runtime.InteropServices.UnmanagedType.LPTStr)> _
     ByVal pszFile As String, ByVal uCommand As Int32, _
     ByVal dwData As Int32) As Int32
    End Function

    Private Sub Button1_Click _
    (ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles Button1.Click
        Dim RetVal As Int32
        Const HH_HELP_CONTEXT = &HF
        Dim hr As New System.Runtime.InteropServices.HandleRef(Me, Me.Handle)
        RetVal = HtmlHelp(hr, "CHM_Checker_Help.chm", _
          HH_HELP_CONTEXT, 10)
    End Sub

When I looked at Help.ShowHelp in System.Windows.Forms using Reflector, I saw that it is a wrapper around hhctrl.ocx as used in the preceding InterOp sample. It takes diversions along the way through private subroutines such as MapCommandToHTMLCommand and ShowHTML10Help.

But why couldn't I get ShowHelp to launch a specific topic?  It seemed like this should work:


    Private Sub Button2_Click _
    (ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles Button2.Click
        Dim intContextID As Integer
        intContextID = 10
       ' Doesn't work!
        Help.ShowHelp(Me, "CHM_Checker_Help.chm", _
        HelpNavigator.Topic, intContextID)
    End Sub

Then I realized that the command value for context ID Help calls needs to be 15 but MapCommandToHTMLCommand was unable to return the command value of 15 when it processed any of the HelpNavigator enum values, including HelpNavigator.Topic. As it turns out, if you hardcode 15 into the ShowHelp routine it works!:

    Private Sub Button2_Click _
    (ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles Button2.Click
        Dim intContextID As Integer
        intContextID = 10
        ' This works but is slow
        Help.ShowHelp(Me, "CHM_Checker_Help.chm", _
        15, intContextID)
    End Sub

When I compared the versions of MapCommandToHTMLCommand in .NET 1.1 and the beta of .NET 2.0, I saw that this oversight/bug has been rectified. The HelpNavigator enumeration in 2.0 includes a new value, HelpNavigator.TopicId. And when MapCommandToHTMLCommand encounters it, it now returns the value of 15 that we need.

BTW, the one thing you'll surely notice is how s-l-o-w Help.ShowHelp() is when you open a context-sensitive topic by passing an integer. When I get more time, I'm going to document the speed difference which probably is caused by wrappers around wrappers and lots of Try/Catch stuff to make sure the Help engine doesn't blow up.

Ken
Microsoft MVP [ASP.NET]

 

1 Comment

Comments have been disabled for this content.