Implementing Context Sensitivity in WinHelp and HTML Help
Paul A. O'Rear of Helpful Solutions and Sageline Softare
Discuss issue as to percentage of attendees w/specific programming questions (i.e. programmers) re. Context sensitive help vs. Those who just want to know a bit more about how context sensitive help works (i.e. help authors). The target audience for the session is the latter, but don't want programmers to go away not having an opportunity to ask questions. Please keep my target in mind, but if you desire more programming related sessions in the future, please indicate that on the evaluation.
The Issues:
Programmers can get caught up with the cool things they're doing in their apps and don't want to spend the time to do the more common 'generic' things of help implementation. Perhaps it borders on the tedious for them.
Tech writers - they've seen some really cool innovative stuff out there in other people's apps and they know that good, cool, useful help systems make even a piece of junk of an app look good - they also should know however that the opposite holds true as well…
-me - I'm smack in the middle of the two - I think that there's nothing cooler than cool apps with cool help, and my interest in programming is in making that happen…
- my intention of targetting help authors for this session is to help authors (J ) get a general grasp as to how context sensitivity works in windows programs in general. This will provide you with enough specifics about how easy it is to implement that you'll have more leverage with your programmers to get it done right. That's the raisened eater for this session (Raison d'etre).
What We're Going To Cover
The Way Windows Works
events and messages, messages and events, events and messages. You get the picture.
Event: Click a button.
Message: Button was clicked.
Event: Select a menu item.
Message: menu item was selected.
Event: minimize a window.
Message: window was minimized.
A event causes a message to be sent to the program in which the event occurred and basically says to the program "Don't just sit there - do something!"
Some messages are automatic - meaning Windows sends the message behind the scenes during normal operations of a program - many of the which the progammer may choose to ignore. The privelege of the programmer is that he may also generate messages or perform other actions as a response to a event/ or on reception of a message.
What Every Help Author Should Know About How Context Sensitive Help Works
Five Common Context Sensitive Help Events
There are 5 common events that are part of the standard interface for obtaining context sensitive help.
What are they?
1. Help button click
2. F1 key press
3. What's this button click
4. context menu via right mouse click.
5. Shift + F1
The Five Events and Their Corresponding Messages
These 5 events generate 3 messages that the programmer can respond to:
1. Help button click => standard button ID message
2. F1 key press=>WM_HELP message
3. What's this button click => WM_HELP message
4. Context menu via right mouse click=>WM_CONTEXTMENU
5. Shift+F1 => WM_HELP
So - if your programmer is going to implement Context sensitive help - what messages do you think he'll need to respond to? J
Basically when the application receives the message, all the programmer needs to do is call the WinHelp or or HTMLHelp API calls with the appropriate parameters. The basic procedure for implementing Context sensitive help for programmers is provided at the end of the handout.
Implementing Context Sensitive Help from the Help Author's Perspective
- WinHelp issues
- HTML Help issues
- Include File Generation Tips
- HTML Help caveats
- Food For Thought…
WinHelp issues
topic ids = control ids in programmers include file (.h)
*TIP* Help Compiler Workshop has a feature that can check your context sensitive help system on the help side for accuracy. (bring up HCW). If you use IDH_ as a prefix for all of your topic ids, then any ids that are found in the include file will be checked to make sure they actually exist in your help system. If they do not you will receive an error message on compile indicating the ID of the item for which there is no topic. Also if you have a topic with at topic id that does not appear in the include file, you'll receive a warning that the topic was not in the include file. HCW also allows you to change the default prefix it will check for if your development team chooses a different standard for identifying their controls.
HTMLHelp issues
Hassle! Since all topics are individual .htm files, it gets messy. You could name each .htm file something like IDH_Dialog_EditBox.htm. Fortunately the HTML Help compiler is smart enough to associate IDH_ ids in an include file with .htm files with the same name.
*NOTE* The HTMLHelp compiler has a bug that it only accepts include files with an ".h" extension - this can really trip you up if you're unaware of it.
Since HTMLHelp only supports plain text popups, if you use the standard means of authoring those popups (as opposed to using a resource DLL as mentioned earlier) you should name that text file cshelp.txt and store it in the root directory of your compiled help file. - That way HTMLHelp will know where to find it and your developer won't have to specify any extra internal path location in the HTMLHelp call. E.g. if in the root directory, the developer can call
HtmlHelp(((LPHELPINFO) lParam) ->hItemHandle, "ourhelp.chm", HH_TP_HELP_WM_HELP, (DWORD) dwIDs);
instead of:
HtmlHelp(((LPHELPINFO) lParam)->hItemHandle, "ourhelp.chm::/popups\cshelp.txt", HH_TP_HELP_WM_HELP, (DWORD) (LPVOID) dwIDs);
The second version is more prone to error.
The format of the cshelp.txt file is a plain text file with topics entered in the following format:
.topic 1
help text for control 1
.topic 2
help text for control 2
Where 1 and 2 after '.topic' indicate the IDH_XXX help ID, or if you've included an .h file you can use the IDH_ names directly like:
.topic IDH_OK
help text for control 1
.topic IDH_CANCEL
help text for control 2
1) no support of rich text popups - only plain text with no extra font attributes.
Solution: Create a hybrid system for your application where Context Sensitive Help is implemented as WinHelp topics and overviews and procedures are implemented as HTMLHelp. (or simply stick with WinHelp…)
2) If you do wish to use HTML Help's plain text popups, you may consider using a special feature that they support which allows you to store the text of these popups as string resources in a resource DLL. Performance may be a little faster using this method as well.
Include file tips
arrange with your developer early on in the development process on a clear naming scheme for control Ids: e.g. IDH_TOOLS_OPTIONSDLG_EDITTAB_OPTIONBTN - that may seem really cumbersome, but when something breaks - you'll know instantly where the problem is - both in the app or in the help file. Another thing to consider is numeric ranges to refer to specific portions of the application. Eg. All controls in a particular dialog box being in the range 100-199, etc.
DBHE.EXE - Dialog Box Help Editor (Microsoft) - comes with later versions of Help Workshop (HCW.EXE) and allows you to read either an app's source .rc or .res files or even an .exe or .dll and generate a listing of all of the dialog's controls and Ids, as well as an array definition that the programmer can use for the WinHelp/HTMLHelp API calls.
For VB or Delphi programs have the developer generate text versions of their forms (which is part of the programming environment's capabilities) and write a Word macro to strip out info you don't need, leaving only the form caption/name and any control names, followed by their HelpContext values and What's This Help values.
HTMLHelp caveats
Food for thought
The WM_HELP and WM_CONTEXTMENU messages which are normally generated by the events that we discussed earlier (remember event! => message!) can also be generated by the developer directly based on whatever other condition may seem appropriate - this can be a means of doing a tip wizard type of functionality - or you may choose to implement your own paradigm for bringing up context sensitive help - issuing these messages manually would be the means of doing that.
Implementing Context Sensitive Help from the Programmer's Perspective
The Help Button
- simply implement the code for the Help button to issue a call to either WinHelp() or HTMLHelp with the following syntax:
WinHelp(hwndBtn, "ourhelp.hlp", HELP_CONTEXT, IDH_THIS_DLG_OVERVIEW);
or
HTMLHELP(hwndBtn, "ourhelp.chm", HH_HELP_CONTEXT, IDH_THIS_DLG_OVERVIEW);
The F1 Key
- to support F1 you need to create a handler for the WM_HELP message and you need to create a DWORD (unsigned long) array of both the Control Ids and the ContextIds in pairs and ending with a pair of 0's to signify the end of the array like:
static DWORD ids[] = {
ID_SAVE, IDH_SAVE,
ID_DELETE, IDH_DELETE,
ID_COPY, IDH_COPY,
ID_PASTE, IDH_PASTE,
0, 0
};
where ID_SAVE is the control ID defined preferably in one header (.h) file and IDH_SAVE is the help context id defined in another header (.h) file.
Then using a member of the HELPINFO structure that gets passed with the WM_HELP message make a call like:
WinHelp(((LPHELPINFO) lParam)->hItemHandle, "ourhelp.hlp", HELP_WM_HELP, (DWORD) (LPVOID) dwArray);
or
HTMLHelp(((LPHELPINFO) lParam)->hItemHandle, "ourhelp.hlp", HH_TP_HELP_WM_HELP, (DWORD) (LPVOID) dwArray);
The HELPINFO structure is part of the Windows API and is defined as:
typedef struct tagHELPINFO { // hi
UINT cbSize;
int iContextType
int iCtrlId;
HANDLE hItemHandle;
DWORD dwContextId;
POINT MousePos;
} HELPINFO, FAR *LPHELPINFO;
The What's This Button
To support What's This Help, technically you can only make the ? button appear on a dialog without minimize and maximize buttons. You have to enable the DS_CONTEXTHELP style of the window as well. Then when your user clicks on the ? button, Windows will take over until you click on a control in the dialog at which point a WM_HELP message will be issued to the dialog and you can handle it identically to the way you'd support the F1 key.
The Right-Click What's This Context Menu
To support What's This Help from the context menu (right-click) you don't need to create a unique popup menu if the only thing you'll have on it is What's This? WinHelp or HTMLHelp will create the popup automatically if you handle the WM_CONTEXTMENU message and call either WinHelp or HTMLHelp. The WM_CONTEXTMENU message passes the handle to control that was selected in its wParam. You can then call WinHelp or HTMLHelp like:
WinHelp((HWND) wParam, "ourhelp.hlp", HELP_CONTEXTMENU, (DWORD)(LPVOID) dwIDs);
or
HTMLHelp((HWND) wParam, "ourhelp.hlp", HH_TP_HELP_CONTEXTMENU, (DWORD)(LPVOID) dwIDs);
The Shift + F1 What's This Functionality
If you're already supporting What's This Help functionality, Shift+F1 should be automatic through the Operating System, otherwise implement it the same as support for What's This Help - by handling the WM_HELP message.
*TIP* If you want your labels automatically associated with their controls for Context Sensitive Help purposes - make sure the label precedes the control in its tab order and then assign the control an ID of -1. WinHelp or HTMLHelp will then use the ID of the next *active* control. Note however that if the control is toggled between being enabled or disabled at run time, then you should manually assign the label a valid ID and use the corresponding IDH in the dword array.
*TIP* If you want to disable context sensitive help for any control, then map the controls ID to a -1 IDH value like:
static DWORD dwIDs = {
ID_GROUP_BOX, ((DWORD) -1)
…
};