One of the things that has suffered most in the desktop environment is the help file. It's easy for developers to become frustrated with the arbitrary "standards" that Apple and Microsoft impose for their help systems and create their own help system--or worse, offer no help at all. However, with a few HTML tricks, it's easy to create completely cross-platform help files.
Paging through help docs is not an enjoyable experience (Microsoft's, especially, sound like they were written by a marketing guru with no programming experience at all). This document is the result of my gleanings from those sources. If you notice an error or omission, email me at cosmicsoft@cosmicsoft.net.
(Disclaimer: the help demos included are only guaranteed to work on recent versions of Mac OS X and Windows. If you want ironclad backwards compatibility, you'll have to do your own research; mostly it just involves building pages that work OK without a CSS parser, plus setting some creator codes for OS 9.)
This tutorial is useless without the sample files I've prepared, so download them here. They contain only HTML, text, CSS, and some graphics.
Both Windows & Mac use HTML for help files. Both also use <META> tags to store info about the file.
On Mac, the only other addition is an "index" file that is used to store info for searches. The folder containing the HTML files and index is then stored in the application bundle and registered and launched with API calls.
On Windows, text-based files for the Table of Contents, Index, and project settings are created, and then all the files are compiled into a proprietary compiled help file (.CHM file) that is launched via custom API calls to open the help book. You must use the proprietary Microsoft HTML Help Compiler to create a .CHM file.
The main differences between the actual HTML pages on the two system are the <META> tags in the header and the style sheet used. The former is not a problem since you can include both in all pages; the Windows tags are ignored on Mac and vice-versa.
The second problem, that of CSS, is worked around by using JavaScript. Based on the presence of "MSIE" in the "navigator" environment variable in JavaScript, a reference to either the Mac-specific stylesheet or the Windows stylesheet is printed in the header.
Why not use CSS conditionals? They're a hack, while Javascript is fairly well recognized. JavaScript is supported on both browsers, although I'm not sure about Mac OS 8, 9, and early versions of 10.
There is a small skeleton of files that must be present in the help folder to do the necessary magic. Herg's a description of each.
This file is the default file for the help book on Macintosh. The example included in this howto is based on a Mac OS X 10.4 "Tiger" help file from Apple Computer, so it's fairly standard. As the default file, it also contains some special info--the help book name, the icon for the help book (used in the Help Viewer menu), and an anchor to identify it as the default.
To specify the menu icon, edit the <META> tag named "AppleIcon". You must include the help book name (unlike most other links); be sure to edit this value when you rename the help book folder.
This file is the default file on Windows. Unlike the Macintosh's default interface, which contains a few "getting started" links, the Windows default page is usually more verbose. It should contain a couple paragraphs describing what your program does; sprinkle some links about various topics into the paragraph.
This is a CSS file used on the Mac. Nothing to edit here, but you can view it if you'd like to see or modify formatting.
This is the CSS file used by Windows.
Mac and Windows help docs use completely different methods for linking to related help documents. On Mac, a simple list of links is displayed; on Windows, a "Related Items" link is displayed, and when click, displays a menu with the related items. To get around this difference, this HOWTO shows the use of a simple Javascript (contained in related.js) that prints out the appropriate system depending on the platform. For more info, see the topictemplate.html file.
This is a special file--the format is dictated by Microsoft--that contains the table of contents for the help book. Needless to say, it's only used on Windows. The format is fairly self-explanatory; it's user-editable psuedo- HTML. This file is referenced in the "WindowsHelp.hhp" file, so if you rename it, be sure to relink it.
This file is also a Windows-only info file; it contains the index. Again, it is just user-editable HTML. It is also linked to from "WindowsHelp.hhp".
This file contains settings for the Microsoft HTML Help compiler. One important setting is the "Compiled file=" setting in the top section; it contains the name of the ".CHM" file that will be outputted.
The only other section that is of any importance is the [FILES] portion. This section must contain all the help pages you are using. Graphics and javascripts don't need to be included; they are automatically detected by the compiler. "mac.css" and "win.css", however, must be included in this section (since they are conditionally included in the HTML file, not by the standard link method).
This file is used on every page on Windows, to "badge" pages so users know which book it's from. On Mac, this graphic is used on the title page (index.html).
This icon is used on Mac in the Help Viewer menu. It's linked to from the index.html file. It must be in PNG format, and you should use transparency (mask) if you want it to look correct.
This is a scaled-down screenshot, used in the Mac default page. If you modify the size of the screenshot, be sure to update index.html.
An example is included in the file "topictemplate.html". It shows how to create a basic document with related items links, a "task list" on Mac, and the header setup. Most of the items are self-explanatory; be sure to fill in the comma-separated keywords and description in the header, and change the page title in both places (the <TITLE> tag and the <h1> section).
For the related items links, the first array is an array of page filenames. These must include the ".html" part of the name. The second array is an array of file titles. You must fill in both, and both arrays must have the exact same number of elements.
On Mac, you must first index the book using Apple's Help Indexer. This is included with Xcode and the Mac Developer Tools. Just drop your help book on to index the book; by default, it only indexes for Tiger, so if you want compatibility with Mac OS 8.6 and later, be sure to check the Preferences.
The complete help folder is simply dropped into the "Contents/Resources" directory of your bundled executable. (Ignore the Apple documentation about needing to put it in "/Contents/Resources/English.lproj" unless you want multiple localized help folders in the same executable.)
Then, add the following items to the Info.plist file in your program:
<key>CFBundleHelpBookFolder</key>
<string>PRODUCTNAME Help</string>
<key>CFBundleHelpBookName</key>
<string>PRODUCTNAME Help</string>
These are very important; without them it will not work. "PRODUCTNAME" must be the same as the name you used in your help HTML files (see the examples), and also the same as the help folder that was dropped into the Resources folder. Note that if you're using REALbasic, you must add these strings to every binary you compile, since RB doesn't let you modify the default Info.plist that it spits out.
On Windows, you must use the Microsoft HTML Help compiler to "compile" your help book. You can download the compiler from the MSDN site; just Google for it. After installing it (it's installed in Program Files, but without a Start menu icon), use it to open the ".hhp" file in your project. Press the compile button, and a compiled help file (".CHM") will be created. This compiled file should be shipped with your application. You can just launch it to open the help book, but it's recommended procedure to use the special API calls (described later).
This step is only necessary on Mac. Every time your application runs, you must call a special Help system function to register the help book (add it to the menu in Help Viewer and make the system aware of it).
The call you need is AHRegisterHelpBook. If you're a C/C++/ObjC developer, I'll let you figure out how to call it. If you're using REALbasic, here's a snippet, courtesy of the REALbasic-NUG archives, for registering your help book:
dim bundleRef as Integer
dim urlRef as Integer
dim FSRef as MemoryBlock
dim OSErr as Integer
#if debugBuild
// Can't register help because the app isn't in a valid bundle
Return
#endif
Declare Function CFBundleGetMainBundle Lib CarbonLib () as Integer
Declare Function CFBundleCopyBundleURL Lib CarbonLib (bundleRef as Integer) as Integer
Declare Function CFURLGetFSRef Lib CarbonLib (urlRef as Integer, fs as Ptr) as Boolean
Declare Function AHRegisterHelpBook Lib CarbonLib (fs as Ptr) as Integer
Declare Sub CFRelease Lib CarbonLib (address as Integer)
bundleRef = CFBundleGetMainBundle()
If bundleRef = 0 Then
System.DebugLog "Help was not successfully registered. (-1)"
Return
End if
urlRef = CFBundleCopyBundleURL(bundleRef)
If urlRef = 0 Then
System.DebugLog "Help was not successfully registered. (-2)"
Return
End if
FSRef = NewMemoryBlock(80)
If CFURLGetFSRef(urlRef, FSRef) then
OSErr = AHRegisterHelpBook(FSRef)
If OSErr <> 0 Then
System.DebugLog "Help was not successfully registered. (OS error " + Str(OSErr) + ")."
End if
Else
System.DebugLog "Help was not successfully registered. (-3)"
End if
CFRelease urlRef
To register your help book, you MUST have the appropriate items added to the .plist in your application bundle. Check back above to make sure you have them, and that the strings are the same as the name of the help folder and the names used in the HTML files.
To show your help book, use the AHGotoPage function on Mac and the HtmlHelp function on Windows. Those declares are in CarbonLib and hhctrl.ocx, respectively.
Here's an example of how to use it, in REALbasic again. If you want to go to the home page (index.html on Mac and overview.html on Windows) pass "" (the empty string) as pagePath. You must include a reference to a parent window, since on Windows help is attached to a "parent window" managed by the OS.
In addition, you will need the Core Foundation classes from Thomas Reed, available at his website.
Sub ShowHelpTopic(pagePath as String, parentWindow as window)
// Open the application's help book.
#if TargetCarbon
dim bundleRef as Integer // CFBundleRef
dim bookName as Integer // CFTypeRef
dim strRef, pagePathRef as CFString
dim err, pagePathRefPtr as Integer
Declare Function CFBundleGetMainBundle Lib CarbonLib () as Integer
Declare Function CFStringCreateWithCString Lib CarbonLib (alloc as Integer, s as CString, encoding as Integer) as Integer
Declare Function CFBundleGetValueForInfoDictionaryKey Lib CarbonLib (bundle as Integer, key as Integer) as Integer
Declare Function CFGetTypeID Lib CarbonLib (cf as Integer) as Integer
Declare Function CFStringGetTypeID Lib CarbonLib () as Integer
Declare Function AHGotoPage Lib CarbonLib (bookName as Integer, path as Integer, anchor as Integer) as Integer
bundleRef = CFBundleGetMainBundle()
If bundleRef = 0 then
msgbox "Error Loading Help."
System.DebugLog "Unable to locate the help files. (-1)"
Return
End if
strRef = new CFString("CFBundleHelpBookFolder")
bookName = CFBundleGetValueForInfoDictionaryKey(bundleRef, strRef.GetReference)
if bookName = 0 or CFGetTypeID(bookName) <> CFStringGetTypeID() then
msgbox "Error Loading Help."
System.DebugLog "Unable to locate the help files. (-2)"
Return
end if
if pagePath = "" then
pagePathRefPtr = 0
else
pagePathRef = new CFString(pagePath)
pagePathRefPtr = pagePathRef.GetReference
end if
err = AHGotoPage(bookName, pagePathRefPtr, 0)
if err <> 0 then
msgbox "Error Loading Help."
System.DebugLog "Unable to locate the help files. (-3)"
Return
end if
#elseif TargetWin32
Declare Function HtmlHelp Lib "hhctrl.ocx" Alias "HtmlHelpA" (hwnd as Integer, url as Ptr, command as Integer, data as integer) as Integer
Declare Function GetDesktopWindow Lib "user32" () As Integer
dim f as FolderItem
f = GetFolderItem("PRODUCTNAMEHelp.chm")
if f is nil then
msgbox "Error loading help."
return
end if
dim theHandle as Integer
if parentWindow <> nil then
theHandle = parentWindow.Handle
else
theHandle = GetDesktopWindow()
end if
if pagePath = "" then pagePath = "overview.html"
dim theURL as MemoryBlock
theURL = new MemoryBlock(LenB(f.AbsolutePath)+lenb(pagePath)+16)
theURL.CString(0) = f.AbsolutePath+"::/"+pagePath
if HTMLHelp(theHandle, theURL, 0, 0) = 0 then
msgbox "Error loading help."
end if
#endif
End Sub
There is, of course, much more to it than just this. But hopefully this tutorial has you started on the path to creating truly cross-platform help files; the days of creating two separate help systems just to support platform-native appearances are over.
There are probably plenty of things I left out. So, if you notice an omission or error, email me at cosmicsoft@cosmicsoft.net to let me know; I'll update this page ASAP.
The information contained herein and the linked download files are given freely to the public domain. The files were originally copied from or based on Apple and Microsoft HTML help files, but there were no copyright notices on those files. You are free to repost, wikify, or otherwise redistribute the content on this site. Just let me know if you can. Thanks!
Adam Ernst, cosmicsoft