Making CHM files work on network drives


On a sunny afternoon about five years ago, Microsoft decided that HTML Help (CHM) files opened from network locations or removable drives are evil. Since then, it has been effectively impossible to open CHM help files from anywhere except a local drive on the local machine.

This has been extremely frustrating for software developers and we are still getting regular support requests about this issue. We have now found a simple solution for this problem that we would like to share with other developers. This article explains what you need to do: It just takes a few lines of code, you don’t need any tricks or Windows Registry hacks and you don’t break any Windows security. It just works.

History of the CHM on network drives problem 

Let’s start with a little background: Microsoft’s “HTML Help” system with its CHM file format for Windows application help was originally introduced in 1998 with Windows 98, and Help & Manual was one of the first authoring tools to embrace it. It has been loved and hated by the help authoring community ever since then, and it has remained almost completely unchanged. To be honest, there are probably more people who hate it than those who love it, but like it or not, HTML Help is still the standard help format for Windows applications and will be for years to come.

Despite its numerous quirks and limitations, the CHM format works – or it did, until Microsoft security update 896358 (http://www.microsoft.com/technet/security/bulletin/ms05-026.mspx) threw a spanner in the works and disabled CHM access on all network drives. Since then, Windows has only allowed CHM files to be opened on local drives. If you try to open them on a network drive or – God forbid – on the Internet, you get this highly informative screen:

Action Canceled ErrorYuck!

The outcry from outraged developers and help authors everywhere was deafening. To be honest, we never really understood the alleged security risks of CHM files on networks – to the best of our knowledge there is no record of a single malware exploit with CHMs. But Microsoft had spoken, the new law was passed and the options available to developers were extremely limited.

Available options after the security update

The most obvious solution is to install your application locally together with its CHM help, which is what Microsoft wants you to do. That works fine, but if your application is server-based you’re stuck up the proverbial smelly creek without a paddle.

There is also a rather complicated Windows Registry tweak that “registers” a specific CHM file or folder as safe for viewing on a network drive. We released a free tool called HHREG (http://www.ec-software.com/products_hhreg.html) to help users apply it, and it was quite popular. However, this tweak needs to be applied to the Registry of every computer that wants to view the help files, which makes it inefficient for general use. Also, it does not work for 32-bit applications running on 64-bit versions of Windows, so it is now even less useful than it was before.

All this was so frustrating that some of our customers even switched from CHM to Help & Manual’s proprietary EXE e-book format for their application help. That works as far as it goes, but EXE e-books were never designed for application documentation and getting them set up to work with context-sensitive help calls is a complicated and annoying process.

Give me a genuinely simple solution!

We were faced with the same problem ourselves for the concurrent edition of Help & Manual, where the application and its help need to be installed on a server drive. The program itself worked fine, but it was no longer possible to display the help. Great. What now?

The Registry fix was not an option for us because it requires administrative permissions. Switching to a different help format was also not an option, because we wanted to follow Windows standards. That meant we either had to find a genuine solution or abandon our server version.

Then we realized that although the application is stored on the server, it runs on the user’s local machine. CHM files are small, and as long as that machine is not a thin client with no storage of its own, all you need to do is make a local copy of the CHM in the user’s temp directory and open it there!

Help is displayed from the local folder for temporary files

Look at the path in this example: Even though the application is loaded from a network share, the help is actually being displayed from the local folder for temporary files.

And that’s really all there is to it. Your application just needs to make a local copy of the CHM on the user’s machine when it executes. Then you direct all your help calls to that copy and everything will work normally, as though security update 896358 had never happened.

The code example below is in Object Pascal, the language that Help & Manual is written in. But whatever language you are working in, whether it be C#, VB.Net or Cobol, you should get the idea right away. It’s a no brainer and it’s also tried and tested: It has been working perfectly for us for over five years, with not a single complaint from a user so far. ;-)

function TForm1.CheckHelpIsLocal: Boolean;

 function GetTempDir: String;
 var
   TmpPath: PChar;
 begin
   try
     GetMem(TmpPath, MAX_PATH);
     FillChar(TmpPath^, MAX_PATH, #0);
     GetTempPath(MAX_PATH, TmpPath);
     Result := ExpandFileName(TmpPath);
   finally
     FreeMem(TmpPath, MAX_PATH);
   end;
 end;

 function GetFileSize(const FileName: String): Integer;
 var
   FileStream: TFileStream;
 begin
   FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
   try
     try
       Result := FileStream.Size;
     except
       Result := 0;
     end;
   finally
     FileStream.Free;
   end;
 end;

var
 strOriginalHelp, strLocalHelp: String;
begin
 if ((copy(Application.HelpFile, 1, 2) = '\\') or (GetDriveType(PChar(extractFilePath(Application.HelpFile))) = DRIVE_REMOTE)) then
 begin
   { Here we go, we need a local copy of HELPMAN.CHM ... First check if a
     copy of the help file is already there (we do not delete this file)
     and if it is, check if it's up-to-date. }

   result := false;
   strOriginalHelp := Application.Helpfile;
   if FileExists(strOriginalHelp) then
   begin
     strLocalHelp := GetTempDir + '\' + extractFileName(Application.Helpfile);

     result := FileExists(strLocalHelp) and (GetFileSize(strOriginalHelp) = GetFileSize(strLocalHelp));
     if not result then
       result := CopyFile(PChar(strOriginalHelp), PChar(strLocalHelp), False);
     if result then
       Application.Helpfile := strLocalHelp;  //assign local help file
   end;
 end
 else
   result := true;
end;
Share this post:

,

Comments are closed.