I have been writing Windows help files for nearly 30 years. It’s a very time consuming process to set up and maintain application help files. And just when I think I have a stable, workable system, Microsoft pulls the rug out from underneath me.
With the introduction of Windows Vista in 2006, Microsoft deprecated WinHelp and no longer includes it with the factory images of Windows Vista, Windows 7 and Windows 8.
HTML_help is Microsoft’s replacement for WinHelp. However, the Windows HTML_help viewer is crippled. It will only open CHM help files on a PC, not those stored on an intranet. Instead of content, the viewer displays the absolutely meaningless “Navigation canceled” message:
I have never understood this. An intranet is an internal network, so why in the world would it be considered a security risk to open a file on your own internal network? Apparently, Internet Explorer (tightly integrated into the Windows OS), is not able to discern the difference between the internet and an intranet.
So, I have been sticking with WinHelp as it is freely available for download from Microsoft for Vista, Windows 7 and (surprisingly) Windows 8, with both 32- and 64- bit versions:
Not until my PC at work was (finally) updated to Windows 7, did I realize that WinHelp is now just as useless as HTML_help. Windows 7 now blocks WinHelp from opening HLP help files stored on an intranet.
Again, WTF? What is the security risk of opening a file on your own internal intranet?
I could not find a trustworthy third party HLP file viewer, so I decided to switch to the CHM help format and seek out a replacement for Microsoft’s HTML_help viewer for intranet applications. After hours of web searching, I eventually found xCHM, a free, open-source, multi-platform, functional replacement for Microsoft’s HTML_help viewer.
In order to utilize xCHM, the help calls in APK had to be rewritten. Logically there are three scenarios:
- If the program is running from a local drive, the default help handling system is called.
- If the program is running on an intranet, and xCHM is available, then xCHM is called to display the relevant help topic.
- If on an intranet, and xCHM is not found, then an error dialog is displayed with a link to web help on rxkinetics.com.
Determine if the program is running locally or via intranet:
01 function TfrmMain.IsOnLocalDrive(): Boolean; 02 var 03 aDrive: string; 04 begin 05 aDrive := ExtractFileDrive(Application.ExeName); 06 if (GetDriveType(PChar(aDrive)) = DRIVE_REMOVABLE) or 07 (GetDriveType(PChar(aDrive)) = DRIVE_FIXED) then 08 Result := True 09 else 10 Result := False; 11 end; 12
Determine which help system is available:
01 function TfrmMain.LocalHelpAvailable(): integer; 02 begin 03 if IsOnLocalDrive then 04 result := 1 05 else 06 begin 07 if XchmInstalled then 08 result := 2 09 else 10 result := 0; 11 end; 12 end;
Intercept the application help call and choose which help system to use:
01 function TfrmMain.ApplicationEvents1Help(Command: Word; Data: Integer; 02 var CallHelp: Boolean): Boolean; 03 var 04 MyHelpKeyWord : string; 05 begin 06 case LocalHelpAvailable of 07 0: begin 08 // On network and xchm is *not* installed 09 CallHelp := False; 10 frmHelp_Link.ShowModal; 11 end; 12 1: CallHelp := True; // Local drive 13 2: begin 14 // On network and xchm *is* installed 15 CallHelp := False; 16 // Get HelpContext of current active control 17 MyHelpKeyWord := IntToStr(Screen.ActiveControl.HelpContext); 18 // If control HelpContext is blank -> Get Parent HelpContext 19 if MyHelpKeyWord='' then 20 begin 21 MyHelpKeyWord := IntToStr(Screen.ActiveControl.Parent.HelpContext); 22 // If parent is blank and parent is not a form, get form HelpContext 23 if MyHelpKeyWord='' then 24 begin 25 if Screen.ActiveControl.Parent <> Screen.ActiveControl.Owner then 26 MyHelpKeyWord := IntToStr(Screen.ActiveForm.HelpContext); 27 // Fallback: if control, parent, and form HelpContext are all blank -> display start up topic 28 if MyHelpKeyWord='' then 29 MyHelpKeyWord := '10'; 30 end; 31 end; 32 ShellToXchm(MyHelpKeyWord); 33 end; 34 end; 35 end;
Shell out to xCHM:
1 procedure TfrmMain.ShellToXchm(HelpTopic:string); 2 var 3 strParameters : string; 4 begin 5 // Create parameter string -- Requires short path name! 6 strParameters := ' /c ' + HelpTopic + ' ' + GetShortName(PathName) + 'apk.chm'; 7 // Shell out to xCHM 8 ShellExecute(Application.Handle, 'open', 'xchm.exe', PChar(strParameters), PChar(PathName), SW_SHOW); 9 end;
Looking back, this has probably been an exercise in futility. It seems as though most pharmacists either don’t know there is a help system built into the program, or they don’t care. Either way, no one has bothered to contact me to report a problem. Why am I not at all surprised?