本文主要講解如何在C++Builder6.0及之前的版本中使用Microsoft的新型幫助:"CHM格式幫助文件",對於如何制作CHM格式幫助,以及如何獲取幫助文件制作工具,本文只會一筆帶過,給出官方鏈接或推薦其它這方面好的教程,就不再贅述。
CHM格式幫助文件制作工具
需要Microsoft的html help workshop來制作CHM格式的幫助,可以到Microsoft站點下載:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp
好的CHM幫助制作教程
學習如何制作CHM格式幫助文檔,請參考:
Html Help Sample in Delphi[卡夫].chm 作者:卡夫[[email protected]]
跟我學HtmlHelp Workshop[馮惠軍][石家莊鐵道學院].chm 作者:馮惠軍[石家莊鐵道學院]
如果想了解更多關於Microsoft Html Help的知識,可以查閱:
Microsoft Html Help Docs.zipMicrosoft官方文檔
更好的是直接在線查閱Microsoft的官方文檔,到MSDN搜索關鍵字:"html help",可以搜索到很多關於Microsoft html help的內容,有最新的html workshop下載、在線API手冊、制作幫助文件示例等等。
到這裡,我假設讀者已經安裝了Microsoft html help workshop,且已掌握如何制作CHM格式幫助文件。
本文中程序中調用的示例幫助文件包含四個主題,這四個主題的源代碼如下:
1001.htm
<html>
<head>
<title>第一個主題</title>
</head>
<body>
<p>第一個主題</p>
</body>
</html>
1002.htm
<html>
<head>
<title>第二個主題</title>
</head>
<body>
<p>第二個主題</p>
</body>
</html>
1003.htm
<html>
<head>
<title>第三個主題</title>
</head>
<body>
<p>第三個主題</p>
</body>
</html>
1004.htm
<html>
<head>
<title>第四個主題</title>
</head>
<body>
<p>第四個主題</p>
</body>
</html>
添加索引“第一個主題”、“第二個主題”、“第三個主題”、“第四個主題”,添加搜索關鍵定“第一個主題”、“第二個主題”、“第三個主題”、“第四個主題”,制作好CHM幫助文件。下一步的工作,是添加此幫助文件對程序UI界面的上下文敏感關系。方法如下:
用文本編輯器寫一個.h文件(頭文件,CHM編譯器需要用它),文件名任意,內容如下:
#define 第一個主題 1001
#define 第二個主題 1002
#define 第三個主題 1003
#define 第四個主題 1004
即把主題“第一個主題”定義為一個數字1001...,向CHM工程文件中添加這個.h文件,方法如下:
(1).打開HTML HELP WorkShop,打開剛才這個CHM工程,在Project頁面上左側,點擊第四個按鈕“HtmlHelp API information”,在彈出的界面上點擊“Alias”,添加主主題的別名,添加“第一個主題”對應html文件1001.htm...;
(2).在剛才的“HtmlHelp API Information”彈出界面上點擊“Map”,點擊右側第一個按鈕“Head Files”,選擇“include”剛才制作的那個.h文件
(3).重新編譯CHM工程,生成的文件就是滿足要求的有上下文敏感關系的CHM幫助文件了。
測試一下您的CHM格式幫助文件是否正確,點擊HTML HELP WorkShop的“Text”->“HTMLHelp API”,在彈出的界面上,浏覽剛才生成的CHM文件,“Command”選擇“HH_HELP_CONTEXT”,第二個參數“Window”默認,第三個參數“Map Numbers”,輸入那個.h文件中定義的數字,點擊確定,就會打開這個CHM文件並激活相關的主題了。
到這為止,已經成功了一半,我們已經用HTML HELP WorkShop制作出了上下文敏感的CHM格式幫助,下一步的重點是在C++Builder和Delphi中來調用這個上下文敏感的CHM幫助文件。
早期的Windows中,Microsoft 為其設計了一套API,用於Windows Help系統,一直到Windows XP SP2仍然保留這套API,它的問題是,只能表現文字等簡單內容,不能象HTML而那樣表現豐富的內容。因此,Microsoft在1998年推出了全新一代的幫助系統:Compiled Help Manual,將HTML文件編譯和壓縮,提供了WEB頁一樣的豐富的表現力,停止了對舊的Windows Help的開發。
Borland C++ Builder 6.0/Delphi7.0及之間的版本的用戶手冊等資源都是舊的Windows Help的,而VCL也只提供了對舊的Windows Help的支持,在當今WEB流行的今天,如果你的軟件還用舊的Windows Help,未免有些老土吧!
我搜索了大量的網絡資源並在季老大的幫助下,完美解決了在C++Builder6.0/Delphi7.0及以前版本中使用上下文敏感的CHM格式幫助的方法。
VCL默認支持舊的上下文敏感的Windows Help,通過設置控件的HelpContext值來指定與控件相關的幫助主題,如果Application收到WM_HELP消息,就會解析WM_HELP消息中的HelpContext,調用Windows Help,顯示相關的主題,因此,調用CHM格式幫助時,我們也需要設置相應控件的HelpContext(值就是上面定義的.h文件中的與主題相關的數值),我們需要截獲發到程序中的WM_HELP消息,分析HelpContext,並調用我們自己制作的CHM幫助,一切就完成了。但是事實卻沒有這麼簡單。
(1).HTML Help的API都封裝在hhctrl.ocx庫中(Windows 98及以後的Windows系統安裝這一個庫),因此,我們使用Borland開發工具中的導入工具implib.exe即可以導入這個庫的靜態版本:implib hhctrl.lib hhctrl.dll,把這個靜態庫添加到我們的工程中,在我們的程序中添加htmlhelp.h頭文件(Borland開發工具已包含這個頭文件),即可以調用HELP Help的API了;
(2).VCL構架的應用程序在以下三種方式中收到WM_HELP消息:在主菜單及其菜單項上按F1、在窗體元素上按F1、在彈出的PopupMenu上按F1,第一種和第二種情況下的消息很容易截取,但是第三種情況卻比較特殊,VCL對PopupMenu消息的處理比較特殊,它封裝了一個不可見的窗體PopupList->Window來統一管理彈出菜單的消息,十分感激季老大,我才截取了第三種方式的WM_HELP消息。
實現的細節如下:
在上述的第一種和第二種方式的WM_HELP消息來源,只要自定義一個WM_HELP消息處理句柄即可,然後根據菜單消息還是其它控件消息來分開處理;
對於上述第三種WM_HELP消息來源,需要在程序運行時重設PopupList->Window的窗體函數,並在程序退出還原,然後在自己定義的這個窗體函數中處理WM_HELP消息。
說再多也沒有代碼有說服力,我實現的代碼如下:
MainFormUnit.h
//---------------------------------------------------------------------------
// 作者:yifei
// 日期:2005-08-31
//---------------------------------------------------------------------------
#ifndef MainFormUnitH
#define MainFormUnitH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Menus.hpp>
#include <ActnList.hpp>
#include <DBGrids.hpp>
#include <ExtCtrls.hpp>
#include <Grids.hpp>
#include <AppEvnts.hpp>
#include <ComCtrls.hpp>
#include <ImgList.hpp>
#include <ToolWin.hpp>
//---------------------------------------------------------------------------
class TMainForm : public TForm
{
__published: // IDE-managed Components
TMainMenu *MMenu;
TMenuItem *F1;
TMenuItem *cdefg1;
TPopupMenu *PPMenu;
TActionList *AList;
TAction *ActionNew;
TAction *ActionOpen;
TAction *ActionClose;
TAction *ActionHelp;
TMenuItem *O1;
TMenuItem *X1;
TMenuItem *H1;
TMenuItem *C1;
TButton *Button1;
TEdit *Edit1;
TButton *Button2;
TComboBox *ComboBox1;
TMenuItem *N1;
TMenuItem *O2;
TMenuItem *X2;
TMenuItem *N2;
TMenuItem *C2;
TLabel *Label1;
TLabel *Label2;
TCheckBox *CheckBox1;
TMenuItem *N3;
TImageList *ImageList;
TToolBar *ToolBar1;
TToolButton *ToolButton1;
TToolButton *ToolButton2;
TToolButton *ToolButton3;
TToolButton *ToolButton4;
TToolButton *ToolButton5;
TToolButton *ToolButton6;
void __fastcall ActionNewExecute(TObject *Sender);
void __fastcall ActionOpenExecute(TObject *Sender);
void __fastcall ActionCloseExecute(TObject *Sender);
void __fastcall ActionHelpExecute(TObject *Sender);
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall FormCreate(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TMainForm(TComponent* Owner);
protected:
// 取消編譯器內聯優化,避免編譯器警告
#pragma option push -vi-
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_HELP, Messages::TMessage, MyHelp)
END_MESSAGE_MAP(TForm)
#pragma option pop
// 自定義的WM_HELP消息處理句柄
void __fastcall MyHelp(Messages::TMessage &Message);
};
//---------------------------------------------------------------------------
extern PACKAGE TMainForm *MainForm;
//---------------------------------------------------------------------------
#endif
MainFormUnit.cpp
//---------------------------------------------------------------------------
// 作者:yifei
// 日期:2005-08-31
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "htmlhelp.h"
#include "MainFormUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
static FARPROC OldMenuProc = NULL; // 保存舊的彈出菜單處理函數
// NewMenuProc 為新的消息處理函數--新的PopupList->Window的窗體函數
static long CALLBACK NewMenuProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::MyHelp(Messages::TMessage &Message)
{
HELPINFO *HI = (HELPINFO*)Message.LParam;
TWinControl *WC;
TMenuItem *MI;
TMenu *Menu;
// 主菜單上傳來的WM_HELP消息
if(HI->iContextType == HELPINFO_MENUITEM)
{
for(int i=0; i<this->ComponentCount; i++)
{
Menu = dynamic_cast<TMenu *>(this->Components[i]);
if(Menu != NULL)
{
MI = Menu->FindItem(HI->iCtrlId, fkCommand);
if(MI != NULL)
{
break;
}
}
}
if(MI != NULL && MI->HelpContext != 0)
{
HtmlHelp(this->Handle, Application->HelpFile.c_str(), HH_HELP_CONTEXT, MI->HelpContext);
}
else
{
HtmlHelp(this->Handle, Application->HelpFile.c_str(), HH_DISPLAY_TOPIC, 0);
}
}
// 活動控件上傳來的WM_HELP消息
else
{
WC = FindControl(HI->hItemHandle);
if(WC != NULL && WC->HelpContext != 0)
{
HtmlHelp(this->Handle, Application->HelpFile.c_str(), HH_HELP_CONTEXT, WC->HelpContext);
}
else
{
HtmlHelp(this->Handle, Application->HelpFile.c_str(), HH_DISPLAY_TOPIC, 0);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ActionCloseExecute(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ActionHelpExecute(TObject *Sender)
{
HtmlHelp(this->Handle, Application->HelpFile.c_str(), HH_DISPLAY_TOPIC, 0);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormClose(TObject *Sender, TCloseAction &Action)
{
// 還原 PopupList 的消息處理函數,必須要還原,否則會有問題
::SetWindowLong(PopupList->Window, GWL_WNDPROC, (long)OldMenuProc);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormCreate(TObject *Sender)
{
// 更改 PopupList 的消息處理函數並保存舊的PopupList的消息處理函數
OldMenuProc = (FARPROC)::SetWindowLong(PopupList->Window, GWL_WNDPROC, (long)NewMenuProc);
}
static long CALLBACK NewMenuProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int n;
int ContextID;
TMenu* PopMenu;
TMenuItem* MenuItem;
// 如果消息不是WM_HELP,直接使用CallWindowProc 這個API把消息傳遞到舊的消息處理函數中處理
if(uMsg != WM_HELP)
return ::CallWindowProc(OldMenuProc, hwnd, uMsg, wParam, lParam);
HELPINFO* phi = (HELPINFO*)lParam;
for(n=0; n<PopupList->Count; n++)
{
PopMenu = (TMenu *)PopupList->Items[n];
if (phi->hItemHandle == PopMenu->Handle)
MenuItem = PopMenu->Items; // 無SubMenu情況
else
MenuItem = ((TPopupMenu*)PopMenu)->FindItem(
(int)phi->hItemHandle, fkHandle); // 有SubMenu的情況
if(MenuItem != NULL)
{
ContextID = PopMenu->GetHelpContext(phi->iCtrlId, true); // iCtrlId保存TMenuItem的Command
if(ContextID == 0)
ContextID = PopMenu->GetHelpContext((int)phi->hItemHandle, false);
if(Screen->ActiveForm==NULL)
break;
if(ContextID == 0)
ContextID = Screen->ActiveForm->HelpContext;
// 這兒彈出 ContextID 的幫助信息
if(ContextID != 0)
{
HtmlHelp(hwnd, Application->HelpFile.c_str(), HH_HELP_CONTEXT, ContextID);
}
else
{
HtmlHelp(hwnd, Application->HelpFile.c_str(), HH_DISPLAY_TOPIC, 0);
}
break;
}
}
return TRUE;
}