How to add support of HTML help files (.chm) on Delphi XE2? We need to use A-links (A-keywords) on HelpContext
property of every control to lookup help pages. Delphi XE2 has native support of HTML help files by unit HTMLHelpViewer
. But how to use it?

- 14,306
- 23
- 105
- 189
-
Thanks, but if you use `HTMLHelpViewer` unit the help stops working on Delphi IDE. – Dmitry Apr 02 '13 at 17:19
-
have you set `Application.HelpFile := 'sample.chm';` pointing to an existing .chm file?? – moskito-x Apr 02 '13 at 20:11
-
BPLs don't know where is .chm file. – Dmitry Apr 03 '13 at 13:56
-
To get sample.chm, there is a download link in my answer !! – moskito-x Apr 03 '13 at 13:58
3 Answers
It's not hard with F1
jump to a context.
Select Edit1 and press F1
. Help opens and Overview.htm is shown.
Prerequisite.
Edit1 Help settings:
sample.chm source settings.
sample.ali
IDH_Overview=Overview.htm
IDH_welcom=FirstTopic.htm
IDH_UsingtheMenus=Overview.htm
sample.h
#define IDH_Creating_Projects_and_Topics 1005
#define IDH_Overview 1003
#define IDH_UsingtheMenus 1009
Unit1.pas
unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, HTMLHelpViewer, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
HHALINKLOOKUP: TButton;
JumpAnchor: TButton;
Edit1: TEdit;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure HHALINKLOOKUPClick(Sender: TObject);
procedure JumpAnchorClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
hpPath : string;
link : HH_AKLINK;
procedure TForm1.FormCreate(Sender: TObject);
begin
hpPath := ExtractFilePath(Application.ExeName) +
'HelpFile\sample.chm';
Application.HelpFile := hpPath;
end;
procedure TForm1.HHALINKLOOKUPClick(Sender: TObject);
var
link : HH_AKLINK;
szUrl,szKey,szMsgText,szMsgTitle,szWindow : AnsiString;
begin
szKey := Edit1.Text; // 'UsingtheMenus';
szUrl :='Overview.htm';
szMsgText :='Error: Can''t find "'+Edit1.Text+'"!';
szMsgTitle :='Error: HH_ALINK_LOOKUP';
szWindow :='main';
with link do begin
cbStruct := sizeof(HH_AKLINK) ;
fReserved := False;
pszKeywords := PChar(szKey);
pszUrl := nil;
pszMsgText := PChar(szMsgText);
pszMsgTitle := PChar(szMsgTitle);
pszWindow := PChar(szWindow);
fIndexOnFail:= False;
end;
HtmlHelpW(0, hpPath+'>main', HH_DISPLAY_TOPIC, DWORD_PTR(nil));
HtmlHelpW(0, hpPath, HH_ALINK_LOOKUP, DWORD_PTR(@link));
end;
procedure TForm1.JumpAnchorClick(Sender: TObject);
begin
HtmlHelpW(0, hpPath+'::/Overview.htm#'+Edit1.Text+'>main', HH_DISPLAY_TOPIC, DWORD(nil));
end;
end.
Here is a ready to use sample.chm and the source Download
There is a trick how to easily, to jump, not only to the .htm file but jumps directly to an anchor.
Change sample.ali
IDH_Overview=Overview.htm
IDH_welcom=FirstTopic.htm
IDH_UsingtheMenus=Overview.htm#UsingtheMenus
Insert an anchor at the place, you want to jump to in Overview.htm
[...]
<A NAME="UsingtheMenus" </A>
<P><STRONG>Using the Menus and Toolbars</STRONG>
<P>The menus and toolbars provide a complete set of tools
[...]
Now it is possible with F1
, jump directly to the desired point in overview.htm.

- 11,832
- 5
- 47
- 60
I suspect that to use A-links you need to do the following:
- Assign an
Application.OnHelp
handler as described below. - Assign
Application.HelpFile
during program startup. - Call
Application.HelpKeyword
if you wish to invoke the help system with an A-link. - Set the
HelpKeyword
property for any GUI controls that you wish to respond to context sensitive F1 key presses.
The OnHelp
handler looks like this:
function TMainForm.ApplicationHelp(Command: Word;
Data: THelpEventData; var CallHelp: Boolean): Boolean;
var
Link: THH_AKLink;
ALink: string;
begin
CallHelp := False;
Result := True;
//argh, WinHelp commands
case Command of
HELP_COMMAND:
begin
ZeroMemory(@Link, SizeOf(Link));
Link.cbStruct := SizeOf(Link);
ALink := PChar(Data); // we are going to re-purpose the keyword as an A-link
Link.pszKeywords := PChar(AnsiString(ALink)); // seems we have to pass a PAnsiChar ..
Link.fIndexOnFail := True;
HtmlHelp(GetDesktopWindow, Application.HelpFile, HH_ALINK_LOOKUP,
DWORD_PTR(@Link));
end;
end;
end;
The HtmlHelpViewer
unit contains methods named LookupALink
which do the same. But I don't see how they could ever be called.
The above approach is a little bit hacky because it interprets keywords as A-Links. If you want context sensitive help, I can't see what else you can do.

- 601,492
- 42
- 1,072
- 1,490
-
But `Application.HelpKeyword` shows help by keyword (a title of help page) - not by A-link (a special code of help page). – Dmitry Mar 07 '13 at 17:11
-
Yep. Didn't spot that nuance. I fixed the answer. I don't think HtmlHelpViewer gets the job done. You need to write your own `OnHelp` handler. I think. I always do that anyway because `HtmlHelpViewer` is so buggy. – David Heffernan Mar 07 '13 at 17:20
-
Thanks! I'll try it now. But does `Application.HelpJump` call help with A-link too? – Dmitry Mar 07 '13 at 17:29
-
No. That resolves to `HH_DISPLAY_TOPIC`. In your question you say you want context sensitive help with different destinations for each control. All you have to work with are the `HelpContext` and `HelpKeyword` properties. The former is no good. It's an integer. Thus you must use `HelpKeyword`. – David Heffernan Mar 07 '13 at 17:36
-
-
You don't. The system does it for you. Call `Application.HelpKeyword` and pass your A-link string. That will route to that event. Remember, what we have in the answer is an event handler. Just like any event handlers, you do not call them directly. – David Heffernan Mar 07 '13 at 17:48
-
Should I add the customed `OnHelp` handler for every form on the project, bpls, dlls or only for the main form? – Dmitry Mar 07 '13 at 17:50
-
Delphi writes `undeclared identifier` for `HtmlHelpViewer.LookupALink`. – Dmitry Mar 07 '13 at 17:52
-
Our project receives WM_MYHELP message from all forms from the application and bpls: `SendMessage(Handle, WM_MYHELP, 0, DWORD(PChar(Self.HelpKeyword)));`. We need to show help by A-Link from this message. – Dmitry Mar 07 '13 at 17:57
-
We need only to call help with A-link string. If I pass A-link string to `Link.pszKeywords` on the code above help opens with standard lookup (not by A-link) and only with the first char of the keyword. – Dmitry Mar 07 '13 at 18:05
-
@Altaveron Slow down! Read the answer. "Should I add the customed OnHelp handler for every form on the project, bpls, dlls or only for the main form?" No, just to `Application.OnHelp`, as I state in the answer. – David Heffernan Mar 07 '13 at 18:08
-
"Delphi writes undeclared identifier for HtmlHelpViewer.LookupALink" Yes. That would happen. But my answer doesn't call `LookupALink`. I merely point out that it cannot be called! Stay on course. – David Heffernan Mar 07 '13 at 18:09
-
Er, I've no idea what `WM_MYHELP` is. Please don't expect me to read your mind. That's not in the question. I think I answered the question that you asked. This comment trail is a disaster already. – David Heffernan Mar 07 '13 at 18:10
-
`HH_ALINK_LOOKUP` is known to work. `HtmlHelp()` is known to work. The code in the answer will connect your app to the help system and use A-links rather than keywords. – David Heffernan Mar 07 '13 at 18:11
-
OK, there's a problem with Unicode/Ansi. That's why only the first character is used. Bear with me. – David Heffernan Mar 07 '13 at 18:30
-
OK, the code in the answer will work. But I'd like to investigate more. I also answered at the question with the bounty. I'll put more detail there when I have it. – David Heffernan Mar 07 '13 at 18:39
-
The actual code is incorrect. There is no `ALink` on the `TMainForm.ApplicationHelp`. – Dmitry Mar 12 '13 at 12:18
-
This code opens help by keyword instead by A-link for me. I don't know why. But I fixed it by setting `CallHelp := False;` on `Application.OnHelp` and by calling help manually with the same code as between `begin` and `end` inside `case`. – Dmitry Mar 12 '13 at 12:30
-
Hmm. The code in the answer already sets `CallHelp` to `False`. – David Heffernan Mar 12 '13 at 12:41
-
I removed all code expept `CallHelp := False` on `OnHelp = ApplicationHelp` method. And call removed code manually. Else F1 (only F1 key) shows keyword help instead A-link help. This issue also exists if the active form is the main application form. And now I need to fix only this issue - all other works fine. – Dmitry Mar 12 '13 at 13:23
-
I fixed this issue by `aLink.fIndexOnFail := False;` instead `True`. But now help doesn't show for the main window. Why? A-link is correct. – Dmitry Mar 12 '13 at 13:47
-
Only `Application.MainFormHandle` as the first parameter for `HtmlHelp` call works incorrectly. – Dmitry Mar 12 '13 at 13:53
-
I don't know. I've not got your project. My simple test project works fine. Are you working with a very small and simple project? – David Heffernan Mar 12 '13 at 13:56
-
The project is very large. But I removed all code calls help before add new one. – Dmitry Mar 12 '13 at 15:53
-
Try starting from an empty project. File | New | VCL Forms App. – David Heffernan Mar 12 '13 at 15:58
-
David, do you know how just open HTML Help on the first page (without lookup)? – Dmitry Mar 12 '13 at 18:32
-
How do you want to identify that page? Have you found the documentation yet? – David Heffernan Mar 12 '13 at 18:42
-
HTML Help has an issue. It never opens the first page by A-link. I checked - link is correct. But HtmlHelp() doesn't open it if `aLink.fIndexOnFail := False` and searching by index if `aLink.fIndexOnFail := True`. So I just need to open the first page without transfering A-link. – Dmitry Mar 12 '13 at 18:44
-
It seems, all A-links with "_" char don't work! At last I found the original issue for all other problems! – Dmitry Mar 12 '13 at 18:47
-
Very strange issue. A-links with '_' char don't work only for the main form. All other A-links work on the main form. All links work on other forms. (Form handler is transfered to HtmlHelp() procedure.) – Dmitry Mar 12 '13 at 19:20
-
Perhaps the reason why you have to convert to AnsiString is that Alinks can only use a very limited character set. Did you try URL encoding the underscore? `'Start%5FPage'` Or is the problem not related to underscores and `Start_Page` is a Klink rather than an Alink? – David Heffernan Mar 12 '13 at 19:21
-
I found that there is no dependency on is it main form or not. The issue depends on `_` char. It exists if I use `_` on .pas source - and there is no issue if I enter `_` on .dfm-file and read it on code. – Dmitry Mar 12 '13 at 19:25
-
You are right. This is not A-link. And not keyword. Something else. May be K-link (but I don't know what it is). So initial problem is that another developer trying to use this other kind of link instead A-links on some places. And all problems became from that. – Dmitry Mar 12 '13 at 19:31
Not sure how Xe2 viewer works (I'm on 2007) but I just use Eric Granges port of the Microsoft HTML help API, which unsurprisingly, is called HTMLhelpAPI.pas.
You can call an Alink using the function
ChmShowTopic(const filename,atopic:string):HWND;

- 1,056
- 1
- 11
- 22
-
No, that uses the `HH_DISPLAY_TOPIC` which is not for A-links. What's more, that port of the HTML Help API is not necessary in modern Delphi since the Delphi supplied header translation has all that's needed. That code dates from 1998!! – David Heffernan Mar 11 '13 at 13:27