Using HTML Help in a MFC Application


Click here to change the theme.

There are three Windows help systems: WinHelp, HTMLHelp Version 1 and HTMLHelp Version 2.

WinHelp is the original help system from 16-bit Windows. WinHelp help files have a HLP extension. Writing the source for a WinHelp help file is especially cumbersome; the source files must be in a RTF file format and must use specific word processing tags in specific ways and in ways that are not obvious or consistent with expectations. Some people might say that it is not intuitive, but intuition means something that is understood without being obvious whereas WinHelp's use of RTF is not easily understood regardless of whether it is obvious or not.

HTMLHelp Version 1 is the help system used most commonly now. Certainly most Microsoft software uses it now. HTMLHelp help files have a CHM extension. HTMLHelp help files are called compiled help files since they are actually many files gathered ("compiled") and compressed together. Usually most of the files are HTML files but not all are. All versions of MFC provided  with Visual C++ versions prior to 7 had support of WinHelp built in. This means that use of HTMLHelp with a MFC application built using VC 6 or prior requires overriding MFC's use of WinHelp.

HTMLHelp Version 2 is new for .Net. It is being used for .Net software but it's future is not yet certain. Microsoft does not (currently) recommend using it with software not designed to use it.

The remainder of this article is a brief introduction to using HTMLHelp and describes how to add very minimal support of HTMLHelp to a MFC application. I have a few simple tips that will help too. This article will not get into context-sensitive help. For the purposes of this article I am using a MFC SDI application generated by the AppWizard without support of Context-sensitive help. Use of HTMLHelp by a MDI application would be the same except the class for the frame window would be different. I am not sure what would be needed for a dialog-based application; I will investigate and describe that further later.

Whenever possible, and when using VC 6 and previous versions of VC to create a new project, if HTMLHelp will be used, then the project should be generated by the AppWizard without the Context-sensitive help option. If a project is generated with the Context-sensitive help option then the HTML Help Workshop can probably help convert the generated help to files for use by HTMLHelp but I don't describe that here. The HTML Help Workshop documentation and other articles can help with that.

Installation of the Microsoft HTML Help SDK

The HTMLHelp SDK is not provided with Visual Studio, at least not version 6. So if you have not installed it separately then you need to, as described in Installing HTML Help. Then look at my Introduction to HTMLHelp article if you need help getting started writing HTML Help files. Then set up the VC environment as described in Including HTML Help Support Files in an Application.

Program Modifications

The following is my version of how to modify a MFC application to use HTMLHelp.

Modifying the Resources

First create string resources to be used as prompts for the menus. Add the following ids with these strings or whatever other strings you choose. The resource editor will probably use the correct "Value" so you should not need to be concerned about that. I don't know if it matters whether the exact values I show here must be used. Also it is probably not necessary to use all 6, probably only ID_HELP_CONTENTS, ID_HELP_INDEX and ID_HELP_SEARCH are necessary.

ID Value Caption
ID_HELP_INDEX 57666 Display the Index of the Help file\nDisplay Index
ID_HELP_FINDER 57667 List Help topics\nHelp Topics
ID_HELP_USING 57668 Display instructions about how to use help\nHelp
ID_HELP 57670 Shows the help file
ID_HELP_SEARCH 57671 Display the Search tab of the Help file\nDisplay Search
ID_HELP_CONTENTS 57672 Display the Table of Contents of the Help file\nDisplay Contents

Next add the following to a menu: ID_HELP_CONTENTS, ID_HELP_INDEX and ID_HELP_SEARCH. It is necessary to add them to a menu first since otherwise they will not be available in the ClassWizard to add handlers for them.

Modifying the Application

In your application's InitInstance add the following so that the help file path and filename are set automatically. This assumes that your chm file is the same filename as your exe file except with a chm extension instead of the exe extension. It should also be in the same directory. That is the convention MFC uses and the following will just change the extension from hlp to chm. Next this code verifies that the chm file exists.

	CString strHelpFile = m_pszHelpFilePath;
strHelpFile.MakeLower();
if (strHelpFile.Right(4) == ".hlp") {
	strHelpFile = strHelpFile.Left(strHelpFile.GetLength()-4) + ".chm";
	free((void*)m_pszHelpFilePath);
	m_pszHelpFilePath = _tcsdup(strHelpFile);
	}
CFileStatus FileStatus;
if (!CFile::GetStatus(m_pszHelpFilePath, FileStatus)) {
	CString Message("Help file not found:\r\n");
	AfxMessageBox(Message+m_pszHelpFilePath);
	return FALSE;		// Optional; this can be removed
	}

Modifying CMainFrame

Use ClassWizard to add handlers for ID_HELP_CONTENTS, ID_HELP_INDEX and ID_HELP_SEARCH then modify them as follows. I don't know where I got the following code for OnHelpSearch from and I am not sure what it does. I should get answers to both those questions.

void CMainFrame::OnHelpContents() {
HWND hWnd = HtmlHelp(0, AfxGetApp()->m_pszHelpFilePath, HH_DISPLAY_TOC, NULL);
}

void CMainFrame::OnHelpIndex() {
HWND hWnd = HtmlHelp(0, AfxGetApp()->m_pszHelpFilePath, HH_DISPLAY_INDEX, NULL);
}

void CMainFrame::OnHelpSearch() {
HH_FTS_QUERY q;
memset(&q, 0, sizeof(HH_FTS_QUERY));
q.cbStruct = sizeof(HH_FTS_QUERY);
q.fUniCodeStrings = FALSE;
q.pszSearchQuery = "";
q.iProximity = HH_FTS_DEFAULT_PROXIMITY;
q.fStemmedSearch = FALSE;
q.fTitleOnly = FALSE;
q.fExecute = TRUE;
q.pszWindow = NULL;
HWND hWnd = HtmlHelp(0, AfxGetApp()->m_pszHelpFilePath, HH_DISPLAY_SEARCH, (DWORD)&q);
}

Then use ClassWizard again to override WinHelp, which is a virtual function, and then modify it as follows.

void CMainFrame::WinHelp(DWORD dwData, UINT nCmd) {
HWND hWnd = HtmlHelp(m_hWnd, AfxGetApp()->m_pszHelpFilePath, nCmd, dwData);
}

Custom Build Step

It is very convenient to have a custom build step for your chm file so that if you change it then it gets copied from wherever it is put by the HTMLHelp compiler to your project's output directory (Debug or Release or whatever) automatically. By using a custom build step, this copy is done whenever you do a build of your VC project but only if the chm file has been changed. To create a custom build step for the help file, first add the file to the project. Then create the custom build step by using the "Custom Build Step" of the project settings. Select the chm file in the tree on the left then enter the following:

Description:
Copying $(InputPath)
Commands:
copy $(InputPath) $(OutDir)\$(InputName).chm
Outputs:
$(OutDir)\$(InputName).chm

References

Microsoft Knowledge Base Articles

140676 - PRB: ClassWizard Uses WM_HELPINFO Instead of WM_HELP simply says that the ClassWizard uses WM_HELPINFO for the WM_HELP message.