Der Aufruf der Hilfedatei geht relativ einfach. Zuerst sollten wir natürlich sicherstellen, dass die Hilfedatei überhaupt vorhanden ist. Ist dies nicht der Fall, deaktivieren wir alle Elemente und Funktionen, die irgendwie mit der Hilfe zu tun haben. Im Beispielprogramm sieht das wie folgt aus:
if(fileexists(szHelpFile) = FALSE) then begin // Buttons deaktivieren EnableWindow(hTocBtn,false); EnableWindow(hIdxBtn,false); EnableWindow(hTheme2Btn,false);
// Hilfe-Button aus der Titelleiste des Fensters // entfernen SetWindowLong(wnd,GWL_EXSTYLE, GetWindowLong(wnd,GWL_EXSTYLE) and not WS_EX_CONTEXTHELP); end;
Wenn wir sicher sind, dass unsere Hilfedatei auch vorhanden ist, dann rufen wir den API-Befehl "WinHelp" auf und lassen uns z.B. das Inhaltsverzeichnis anzeigen:
if(IsWindowEnabled(hTocBtn)) then WinHelp(wnd,@szHelpFile[1],HELP_FINDER,0);
Um gezielt eine bestimmte Hilfeseite aufzurufen, müssen Sie diese gemappt haben, da die Windows-Hilfe mit numerischen Werten arbeitet (s. hier). In unserem Beispiel haben wir der zweiten Hilfeseite die ID 2000 zugeordnet, so dass wir diese Seite jetzt anzeigen können:
if(IsWindowEnabled(hTheme2Btn)) then begin URL := szHelpFile + '>HLPWIN'; WinHelp(wnd,@URL[1],HELP_CONTEXT,2000); end;
Das Beispiel zeigt Ihnen u.a. auch, wie man die Ausgabe eines Hilfethemas in ein definiertes Fenster umleitet. Wenn Sie kein Fenster definiert haben, benutzen Sie stattdessen nur den Namen der Hilfedatei zur Übergabe an den API-Befehl
Die Taste F1 wird üblicherweise zur Anzeige der Hilfedatei eines Programms verwendet. Allerdings (und dazu muss ich leider ein wenig auf das nächste Kapitel vorgreifen) dient F1 auch dazu, Kontextinformationen zu einem aktiven Control anzuzeigen. Wir müssen also unterscheiden, wann welche Hilfe erwünscht ist.
Dabei hilft uns das THelpInfo-Record, auf das wir über den Zeiger im lParam zugreifen können, wenn wir die Nachricht "WM_HELP" auswerten. Da uns hier nur die Anzeige der Hilfedatei interessiert (die kontextsensitive Hilfe folgt im nächsten Kapitel, wie gesagt!), prüfen wir, ob das im Record übermittelte Handle mit dem Fenster-Handle identisch ist. Ist das der Fall, dann senden wir lediglich einen Buttonklick mit der ID des Hilfe-Buttons an das Programm. Dadurch sparen wir auch gleich noch Code:
WM_HELP:
if(PHelpInfo(lp)^.hItemHandle = wnd) then
SendMessage(wnd,WM_COMMAND,MAKELONG(IDC_TOCBTN,BN_CLICKED),0)
else
Result := DefWindowProc(wnd,uMsg,wp,lp);
Hier gibt es eigentlich zwei Möglichkeiten. Die erste ist die Benutzung der Befehle "MessageBox" bzw. "MessageBoxEx". Um die Hilfe hier verwenden zu können, benutzen Sie das Flag MB_HELP bei der Angabe des Stils, beispielsweise
MessageBox(wnd,'Klicken Sie auf den Hilfe-Button',
AppName,
MB_OKCANCEL or MB_HELP);
Wenn der Anwender diesen Button anklickt oder F1 drückt, wird die Nachricht "WM_HELP" an das Besitzerfenster (unsere Anwendung) geschickt. Und damit ergibt sich ein kleines Problem. Wie Sie etwas weiter oben sehen können, prüfen wir in "WM_HELP" bisher nur, ob das übermittelte Handle dem Handle der Anwendung entspricht. Diesmal ist es aber das Handle der Dialogbox, und deshalb müssen wir die Funktion noch etwas erweitern:
if(PHelpInfo(lp)^.hItemHandle = wnd) or
(GetParent(PHelpInfo(lp)^.hItemHandle) = wnd)
then
SendMessage(wnd,WM_COMMAND,MAKELONG(IDC_TOCBTN,BN_CLICKED),0)
Voilą.
Ich möchte aber noch auf eine etwas elegantere Methode hinweisen, bei der die Dialogbox auch ein ganz bestimmtes Thema der Hilfe anzeigen kann. Dazu benötigen wir aber "MessageBoxIndirect", die die Dialogbox mit Hilfe eines TMsgBoxParams-Records erzeugt:
const HLP_PAGE_4 = 3000; var msgbox : TMsgBoxParams = ( cbSize:sizeof(TMsgBoxParams); lpszText:'Klicken Sie auf den Hilfe-Button'; lpszCaption:AppName; dwStyle:MB_OKCANCEL or MB_HELP or MB_ICONINFORMATION; dwContextHelpId:HLP_PAGE_4; );
Sie sehen hier die Membervariable dwContextHelpId, die zur Anzeige eines ausgewählten Themas der Hilfedatei benutzt wird. In diesem Fall ist das eine Seite, die den gemappten Wert 3000 besitzt. Um die Dialogbox dann aufzurufen, setzen wir zunächst noch die Werte des Besitzerfensters und des Instanzenhandles, dann benutzen wir o.g. Funktion und übergeben als Parameter das TMsgBoxParams-Record:
msgbox.hwndOwner := wnd; msgbox.hInstance := hInstance; MessageBoxIndirect(msgbox);
Wenn Sie die Dialogbox nun anzeigen lassen und auf den Hilfe-Button klicken, dann erscheint der Text des gemappten Themas als Popup. Das liegt daran, dass das Beispielprogramm natürlich schon die Behandlung der kontextsensitiven Hilfe enthält. Möchten Sie das Thema stattdessen in einem normalen Hilfefenster sehen? Dann müssen Sie eine Callback-Prozedur verwenden, weil ansonsten -wie oben!- die Nachricht "WM_HELP" an das Besitzerfenster geschickt wird. Und weil das Programm in dem Fall eine Kontext-ID übermittelt bekommt, reagiert es natürlich entsprechend.
Die Callback-Prozedur sieht wie folgt aus:
procedure MsgBoxCallback(phi: PHelpInfo); stdcall; begin URL := szHelpFile + '>HLPWIN'; WinHelp(phi.hItemHandle,@URL[1],HELP_CONTEXT,phi.dwContextId); end;
Wir legen hier lediglich das Zielfenster fest und benutzen den üblichen Aufruf von "WinHelp" zur Anzeige eines einzelnen Themas. Zur Verwendung dieser Umleitung setzen wir die lpfnMsgBoxCallback-Membervariable des TMsgBoxParams-Records vor dem Aufruf von "MessageBoxIndirect" auf unsere Prozedur:
msgbox.lpfnMsgBoxCallback := @MsgBoxCallback;
Damit erscheint das gewünschte Hilfethema wie gewohnt in einem eigenen Fenster. Das Beispielprogramm demonstriert das mit Hilfe des Compilerschalters "NOPOPUP", der normalerweise aktiv ist und nach obigem Muster das Hilfethema in einem eigenen Fenster anzeigt. Wenn Sie allerdings die Anweisung
{$DEFINE NOPOPUP}
mit einem Punkt sozusagen "deaktivieren", wird die Hilfe-Information als Popup gezeigt.