C++Builder 6 / Delphi 7 的 VCL 僅原生支援舊式 WinHelp(.hlp),面對功能更強、格式更現代的 HTML Help(.chm)無能為力。本文詳細記錄從 CHM 製作、別名/對應表建立、hhctrl.ocx 靜態庫導入、WM_HELP 訊息攔截、彈出選單特殊處理到完整程式碼整合的全流程,實現控制項層級的上下文敏感說明。
1001.htm...)對應主題 ID。helpids.h 定義符號常數:
#define IDH_TOPIC1 1001
#define IDH_TOPIC2 1002
#define IDH_TOPIC3 1003
#define IDH_TOPIC4 1004
.h,於 Alias 頁面建立別名(如 IDH_TOPIC1=1001.htm),於 Map 頁面匯入 .h 產生對應表。HH_HELP_CONTEXT + Map Number 驗證跳轉正確。VCL 在三種情況發送 WM_HELP:
TPopupMenu)按 F1 —— 此訊息由隱藏視窗 PopupList->Window 統一分派前兩類易於在表單 Message Map 攔截;第三類需在執行期 子類別化 PopupList->Window 的視窗程序。
MainFormUnit.hclass TMainForm : public TForm {
__published: // IDE 元件...
private:
public:
__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
void __fastcall MyHelp(Messages::TMessage &Message);
};
MainFormUnit.cpp#include <htmlhelp.h> // 需先 implib hhctrl.lib hhctrl.ocx 並加入專案
static FARPROC OldMenuProc = NULL;
// 新視窗程序:處理 PopupMenu 的 WM_HELP
static long CALLBACK NewMenuProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg != WM_HELP) return CallWindowProc(OldMenuProc, hwnd, uMsg, wParam, lParam);
HELPINFO* phi = (HELPINFO*)lParam;
for (int n=0; nCount; ++n) {
TMenu* popMenu = (TMenu*)PopupList->Items[n];
TMenuItem* item = (phi->hItemHandle == popMenu->Handle) ?
popMenu->Items :
((TPopupMenu*)popMenu)->FindItem((int)phi->hItemHandle, fkHandle);
if (item) {
int ctx = popMenu->GetHelpContext(phi->iCtrlId, true);
if (!ctx) ctx = popMenu->GetHelpContext((int)phi->hItemHandle, false);
if (!ctx && Screen->ActiveForm) ctx = Screen->ActiveForm->HelpContext;
HtmlHelp(hwnd, Application->HelpFile.c_str(),
ctx ? HH_HELP_CONTEXT : HH_DISPLAY_TOPIC, ctx);
break;
}
}
return TRUE;
}
// 表單建立:子類別化 PopupList->Window
void __fastcall TMainForm::FormCreate(TObject *Sender) {
OldMenuProc = (FARPROC)SetWindowLong(PopupList->Window, GWL_WNDPROC, (long)NewMenuProc);
}
// 表單關閉:還原視窗程序
void __fastcall TMainForm::FormClose(TObject *Sender, TCloseAction &Action) {
SetWindowLong(PopupList->Window, GWL_WNDPROC, (long)OldMenuProc);
}
// 通用 WM_HELP 處理:主選單、一般控制項
void __fastcall TMainForm::MyHelp(Messages::TMessage &Message) {
HELPINFO* HI = (HELPINFO*)Message.LParam;
if (HI->iContextType == HELPINFO_MENUITEM) { // 主選單
TMenuItem* mi = NULL;
for (int i=0; iFindItem(HI->iCtrlId, fkCommand))) break;
}
HtmlHelp(Handle, Application->HelpFile.c_str(),
mi && mi->HelpContext ? HH_HELP_CONTEXT : HH_DISPLAY_TOPIC,
mi ? mi->HelpContext : 0);
} else { // 一般控制項
TWinControl* wc = FindControl(HI->hItemHandle);
HtmlHelp(Handle, Application->HelpFile.c_str(),
wc && wc->HelpContext ? HH_HELP_CONTEXT : HH_DISPLAY_TOPIC,
wc ? wc->HelpContext : 0);
}
}
// 工具列/選單項直接呼叫說明
void __fastcall TMainForm::ActionHelpExecute(TObject *Sender) {
HtmlHelp(Handle, Application->HelpFile.c_str(), HH_DISPLAY_TOPIC, 0);
}
| 環節 | 做法 |
|---|---|
| API 導入 | implib hhctrl.lib hhctrl.ocx → 專案加入 hhctrl.lib,包含 htmlhelp.h |
| HelpFile 設定 | Application->HelpFile = "MyApp.chm"(專案選項或執行期指定) |
| 控制項 HelpContext | 物件檢視器設定對應 Map Number(如 1001) |
| 主選單/控制項 | 表單 WM_HELP 處理器 MyHelp 統一分派 |
| 彈出選單 | FormCreate 子類別化 PopupList->Window,FormClose 還原 |
| 訊息參數 | HELPINFO.iContextType 區分選單/控制項;iCtrlId/hItemHandle 定位項目 |