第287章 HTMLヘルプの通知メッセージ


HTMLヘルプからの通知メッセージを受け取るにはどうしたらよいのでしょうか。 これは、HtmlHelp呼び出してhwndCallerがNULLでないことと、ウィンドウタイプで idNotifyが0以外に設定されている必要があります。



今回は、HTMLヘルプを呼び出して、ここから通知メッセージを受け取り これをクライアント領域に表示するプログラムを作ります。

HTMLヘルプが起動したとき、トピックペインのページを移動したとき、 HTMLヘルプのボタンを押したときなどに通知メッセージが送られてきます。



通知メッセージの種類は次の表を参照してください。

通知メッセージlParam説明
HHN_WINDOW_CREATEHHN_NOTIFY構造体へのポインタヘルプウィンドウが作られる直前
HHN_NAVCOMPLETEHHN_NOTIFY構造体へのポインタchmファイルのナビゲートが成功したとき
HHN_TRACKHHNTRACK構造体へのポインタツールバーのボタンが押されたとき
ナビゲーションペインのタブが押されたとき

さて、HHN_NOTIFY構造体は次のように定義されています。

typedef struct tagHHN_NOTIFY { NMHDR hdr; PCSTR pszUrl; } HHN_NOTIFY;

hdrは毎度おなじみのNMHDR構造体です。

pszUrlは、移動先の名前です。

HHNTRACK構造体は次のように定義されています。

typedef struct tagHHNTRACK { NMHDR hdr; PCSTR pszCurUrl; int idAction; HH_WINTYPE* phhWinType; } HHNTRACK;

hdrはNMHDR構造体です。

pszCurUrlは、現在のトピックです。

idActioは、ユーザーが押したボタンを表します。これは 次の表を参照してください。

押されたボタン / タブ
HHACT_BACK「戻る」ボタン
HHACT_CONTRACT(ナビゲーションペインを)「隠す」ボタン
HHACT_EXPAND(ナビゲーションペインを)「表示する」ボタン
HHACT_FORWARD「進む」ボタン
HHACT_HOME「ホーム」ボタン
HHACT_JUMP1「JUMP1」ボタン
HHACT_JUMP2「JUMP2」ボタン
HHACT_OPTIONS「オプションボタン」
実際には来ません
HHACT_PRINT「印刷」ボタン
HHACT_REFRESH「更新」ボタン
HHACT_STOP「中止」ボタン
HHACT_SYNC「同期」ボタン
HHACT_TAB_CONTENTSHHACT_TAB_*は来ません
HHACT_TAB_FAVORITES
HHACT_TAB_INDEX
HHACT_TAB_SEARCH
HHACT_ZOOM「フォント」ボタン

phhWinTypeは、HH_WINTYPE構造体へのポインタです。

では、プログラムを見てみましょう。

// help03.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "ヘルプ(&H)" BEGIN MENUITEM "目次(&C)", IDM_MOKUJI MENUITEM "キーワード(&I)", IDM_INDEX MENUITEM "検索(&S)", IDM_SEARCH MENUITEM "ヘルプの最大化(&X)", IDM_MAX MENUITEM "キーワード検索(&K)", IDM_KEY MENUITEM "ヘルプを閉じる(&E)", IDM_HELPEND END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 187, 73 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "キーワード入力" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,7,7,173,15,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,7,52,50,14 PUSHBUTTON "キャンセル",IDCANCEL,129,52,50,14 LTEXT "キーワードはセミコロンで区切って複数入力できます", IDC_STATIC,7,30,173,13 PUSHBUTTON "クリア",IDC_BUTTON1,68,52,50,14 END

メニューとダイアログボックスのリソース・スクリプトです。

// help03.cpp #ifndef STRICT #define STRICT #endif #define HTMLHELP "e:\\MyDocument\\htmlhelp\\novo\\insulin.chm" #define ID_NOTIFY 1000 #define ID_EDIT 100 #include <windows.h> #include <windowsx.h> #include <htmlhelp.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); HINSTANCE hInst; BOOL MyCreateWinType(HWND, char *); BOOL MyScroll(HWND); DWORD dwCookie; //クッキー char szClassName[] = "help03"; //ウィンドウクラス char szKeyWords[1024]; //キーワード入力用

htmlhelp.libをプロジェクトに参加させるのを忘れないでください。

ID_NOTIFYをdefineしていることに注意してください。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; HtmlHelp(NULL, NULL, HH_INITIALIZE, (DWORD)&dwCookie); while (GetMessage(&msg, NULL, 0, 0)) { if (!HtmlHelp(NULL, NULL, HH_PRETRANSLATEMESSAGE, (DWORD)&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; }

HH_INITIALIZE, HH_PRETRANSLATEMESSAGEコマンドをここで実行してください。

//ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるヘルプ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

これは、いつもと同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HH_FTS_QUERY q; char szHelp[256]; HWND hHelp; static int wx, wy; HH_AKLINK ak; NMHDR *lpnmhdr; static HH_WINTYPE *pWinType, hWinType; static HWND hEdit; char *lpszStr; int nSize; HGLOBAL hMem; HHN_NOTIFY *lphhn; HHNTRACK *lphhnTrack; switch (msg) { case WM_CREATE: wx = GetSystemMetrics(SM_CXSCREEN); wy = GetSystemMetrics(SM_CYSCREEN); hEdit = CreateWindow("Edit", "", WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_HSCROLL | WS_VSCROLL, 0, 0, 0, 0, hWnd, (HMENU)ID_EDIT, hInst, NULL); break; case WM_SIZE: MoveWindow(hEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); break; case WM_NOTIFY: lpnmhdr = (LPNMHDR)lp; if (lpnmhdr->idFrom != ID_NOTIFY) return DefWindowProc(hWnd, msg, wp, lp); switch (lpnmhdr->code) { case HHN_NAVCOMPLETE: nSize = Edit_GetTextLength(hEdit); hMem = GlobalAlloc(GHND, (DWORD)nSize + 256); if (hMem == NULL) MessageBox(hWnd, "Error HHN_NAVCOMP", "OK", MB_OK); lpszStr = (char *)GlobalLock(hMem); Edit_GetText(hEdit, lpszStr, nSize + 256); strcat(lpszStr, "\r\n"); strcat(lpszStr, "HHN_NAVCOMPLETE通知メッセージを受け取りました\r\n"); strcat(lpszStr, "---"); lphhn = (HHN_NOTIFY *)lp; strcat(lpszStr, lphhn->pszUrl); Edit_SetText(hEdit, lpszStr); MyScroll(hEdit); GlobalUnlock(hMem); GlobalFree(hMem); break; case HHN_TRACK: nSize = Edit_GetTextLength(hEdit); hMem = GlobalAlloc(GHND, (DWORD)nSize + 256); lpszStr = (char *)GlobalLock(hMem); Edit_GetText(hEdit, lpszStr, nSize + 256); strcat(lpszStr, "\r\n"); strcat(lpszStr, "HHN_TRACK通知メッセージを受け取りました\r\n"); strcat(lpszStr, "---"); lphhnTrack = (HHNTRACK *)lp; switch (lphhnTrack->idAction) { case HHACT_BACK: strcat(lpszStr, "Backボタンが押されました"); break; case HHACT_CONTRACT: strcat(lpszStr, "Hideボタンが押されました"); break; case HHACT_EXPAND: strcat(lpszStr, "Showボタンが押されました"); break; case HHACT_FORWARD: strcat(lpszStr, "Fowardボタンが押されました"); break; case HHACT_HOME: strcat(lpszStr, "Homeボタンが押されました"); break; case HHACT_OPTIONS://オプションボタンを押しても無効です strcat(lpszStr, "オプションボタンが押されました"); break; case HHACT_PRINT: strcat(lpszStr, "印刷ボタンが押されました"); break; case HHACT_REFRESH: strcat(lpszStr, "更新ボタンが押されました"); break; case HHACT_STOP: strcat(lpszStr, "中止ボタンが押されました"); break; case HHACT_SYNC: strcat(lpszStr, "同期ボタンが押されました"); break; case HHACT_ZOOM: strcat(lpszStr, "ズームボタンが押されました"); break; default: strcat(lpszStr, "ボタンが押されました"); break; } Edit_SetText(hEdit, lpszStr); MyScroll(hEdit); GlobalUnlock(hMem); GlobalFree(hMem); break; case HHN_WINDOW_CREATE: nSize = Edit_GetTextLength(hEdit); hMem = GlobalAlloc(GHND, (DWORD)nSize + 256); if (hMem == NULL) MessageBox(hWnd, "Error", "OK", MB_OK); lpszStr = (char *)GlobalLock(hMem); Edit_GetText(hEdit, lpszStr, nSize + 256); strcat(lpszStr, "\r\n"); strcat(lpszStr, "HHN_WINDOW_CREATE通知メッセージを受け取りました"); Edit_SetText(hEdit, lpszStr); MyScroll(hEdit); GlobalUnlock(hMem); GlobalFree(hMem); break; } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_KEY: if (HtmlHelp(hWnd, HTMLHELP, HH_GET_WIN_HANDLE, (DWORD)"MyWin01") == NULL) MyCreateWinType(hWnd, "MyWin01"); id = DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); if (id == IDCANCEL) break; memset(&ak, 0, sizeof(HH_AKLINK)); ak.cbStruct = sizeof(HH_AKLINK); ak.pszKeywords = szKeyWords; ak.pszMsgText = "そのようなキーワードはありません"; ak.pszMsgTitle = "エラー"; HtmlHelp(hWnd, HTMLHELP, HH_KEYWORD_LOOKUP, (DWORD)&ak); break; case IDM_MAX: if (HtmlHelp(hWnd, HTMLHELP, HH_GET_WIN_HANDLE, (DWORD)"MyWin01") == NULL) MyCreateWinType(hWnd, "MyWin01"); hHelp = HtmlHelp(hWnd, HTMLHELP, HH_GET_WIN_HANDLE, (DWORD)"MyWin01"); if (hHelp) MoveWindow(hHelp, 0, 0, wx, wy, TRUE); else MessageBox(hWnd, "ヘルプは表示されていません", "Error", MB_OK); break; case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_MOKUJI: if (HtmlHelp(hWnd, HTMLHELP, HH_GET_WIN_HANDLE, (DWORD)"MyWin01") == NULL) MyCreateWinType(hWnd, "MyWin01"); strcpy(szHelp, HTMLHELP); strcat(szHelp, "::/oldex/q01.htm>MyWin01"); hHelp = HtmlHelp(hWnd, szHelp, HH_DISPLAY_TOC, NULL); break; case IDM_INDEX: if (HtmlHelp(hWnd, HTMLHELP, HH_GET_WIN_HANDLE, (DWORD)"MyWin01") == NULL) MyCreateWinType(hWnd, "MyWin01"); HtmlHelp(hWnd, HTMLHELP, HH_DISPLAY_INDEX, (DWORD)"ガラス球"); break; case IDM_SEARCH: if (HtmlHelp(hWnd, HTMLHELP, HH_GET_WIN_HANDLE, (DWORD)"MyWin01") == NULL) MyCreateWinType(hWnd, "MyWin01"); //正しく動作しません APIのバグであるとMSDNに出ていました(ID: Q241381) memset(&q, 0, sizeof(HH_FTS_QUERY)); q.cbStruct = sizeof(HH_FTS_QUERY); q.fUniCodeStrings = FALSE; q.pszSearchQuery = "ABC"; q.iProximity = HH_FTS_DEFAULT_PROXIMITY; q.fStemmedSearch = FALSE; q.fTitleOnly = FALSE; q.fExecute = FALSE; q.pszWindow = "MyWin01"; HtmlHelp(hWnd, HTMLHELP, HH_DISPLAY_SEARCH, (DWORD)&q); break; case IDM_HELPEND: HtmlHelp(NULL, NULL, HH_CLOSE_ALL, 0); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { HtmlHelp(NULL, NULL, HH_UNINITIALIZE, (DWORD)dwCookie); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

メインウィンドウのプロシージャです。

WM_NOTIFYメッセージが来たらNMHDR構造体のidFromメンバを調べます。 これが、最初に定義したID_NOTIFYでないときはDefWindowProcに任せます。

ID_NOTIFYの時はNMHDR構造体のcodeメンバで場合分けします。

通知メッセージがHHN_NAVCOMPLETEの時は、エディットコントロールに表示されている 文字列の長さを調べてこれより256バイト多くメモリを確保します。 そして、HHN_NAVCOMPLETEメッセージが来たことと、HHN_NOTIFY構造体のpszUrlメンバを 文字列に追加します。

HHN_TRACK通知メッセージが来たときは、その旨を文字列に追加して、さらに HHNTRACK構造体のidActionメンバを調べて、その意味を文字列に追加します。

HHN_WINDOW_CREATE通知メッセージが来たときは、その旨を文字列に追加します。

メニューからヘルプ関係の項目が選択されたらHH_GET_WIN_HANDLEコマンドで ヘルプが表示されているかどうか調べて、表示されていないときは自作関数 MyCreateWinTypeでウィンドウタイプを設定します。

LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hEdit; switch (msg) { case WM_INITDIALOG: hEdit = GetDlgItem(hDlg, IDC_EDIT1); Edit_SetText(hEdit, szKeyWords); SetFocus(hEdit); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(hEdit, szKeyWords, sizeof(szKeyWords)); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; case IDC_BUTTON1: Edit_SetText(hEdit, ""); SetFocus(hEdit); return TRUE; } return FALSE; } return FALSE; }

ダイアログボックスのプロシージャです。これは、第285章のものと同じです。

BOOL MyCreateWinType(HWND hWnd, char *lpszWinType) { HH_WINTYPE m_hhWinType; ZeroMemory(&m_hhWinType, sizeof(HH_WINTYPE)); m_hhWinType.cbStruct = sizeof(HH_WINTYPE); m_hhWinType.fsWinProperties = HHWIN_PROP_TRI_PANE | HHWIN_PROP_AUTO_SYNC | HHWIN_PROP_TAB_ADVSEARCH | HHWIN_PROP_TAB_FAVORITES | HHWIN_PROP_TAB_SEARCH | HHWIN_PROP_TRACKING; m_hhWinType.fsToolBarFlags = HHWIN_BUTTON_BACK | HHWIN_BUTTON_HOME | HHWIN_BUTTON_FORWARD | HHWIN_BUTTON_EXPAND | HHWIN_BUTTON_OPTIONS | HHWIN_BUTTON_PRINT | HHWIN_BUTTON_REFRESH | HHWIN_BUTTON_SYNC | HHWIN_BUTTON_ZOOM | HHWIN_BUTTON_STOP; //HHWIN_BUTTON_SEARCH, HHWIN_BUTTON_INDEX は現在正しくありません。(インプリメントされていない) //HHWIN_BROWSE_**は定義されていません m_hhWinType.pszFile = HTMLHELP; m_hhWinType.pszHome = "mk:@MSITStore:e:\\MyDocument\\HTMLHELP\\NOVO\\INSULIN.chm::/index.htm"; m_hhWinType.pszToc = "e:\\MyDocument\\htmlhelp\\novo\\insulin.hhc"; m_hhWinType.pszIndex = "e:\\MyDocument\\htmlhelp\\novo\\insulin.hhk"; m_hhWinType.iNavWidth = 175; m_hhWinType.nShowState = SW_RESTORE; m_hhWinType.curNavType = HHWIN_NAVTYPE_TOC | HHWIN_NAVTYPE_SEARCH | HHWIN_NAVTYPE_INDEX | HHWIN_NAVTYPE_FAVORITES ; m_hhWinType.tabpos = HHWIN_NAVTAB_BOTTOM; m_hhWinType.idNotify = ID_NOTIFY; m_hhWinType.pszCaption= "インスリン"; m_hhWinType.fsValidMembers = HHWIN_PARAM_STYLES | HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_RECT | HHWIN_PARAM_TB_FLAGS | HHWIN_PARAM_NAV_WIDTH | HHWIN_PARAM_SHOWSTATE | HHWIN_PARAM_TABPOS | HHWIN_PARAM_CUR_TAB; m_hhWinType.pszType = lpszWinType; HtmlHelp(hWnd, HTMLHELP, HH_SET_WIN_TYPE, (DWORD) &m_hhWinType); return TRUE; }

ウィンドウタイプを設定する関数です。 HH_WINTYPE構造体については第286章を参照してください。

ウィンドウタイプを設定するにはHH_SET_WIN_TYPEコマンドを使います。

HH_SET_WIN_TYPE コマンド pszFileは、chmファイルを指定します。 dwDataは、HH_WINTYPE構造体へのポインタを指定します。

ちょっと面倒くさいですが、1つずつ見ていくとたいして難しくはありません。 ただ、API自体のバグ等が多数あるので思い通りに動かないときは自分の プログラムが悪いのかどうか、なかなか判断がつかない場合もあります。

tabposメンバにHHWIN_NAVTAB_BOTTOMを設定していることに注意してください。 こうするとナビゲーションペインのタブが下側につきます。

HHWIN_NAVTAB_LEFTというのもありますが、タブは左側に表示されますが これもバグがあって正しく働きません。

BOOL MyScroll(HWND hWnd) { int iLine; iLine = Edit_GetLineCount(hWnd); SendMessage(hWnd, EM_LINESCROLL, 0, (LPARAM)iLine - 1); return TRUE; }

エディットコントロールに文字列を追加したとき、スクロールして追加した 文字が見えるようにする関数です。第281章で作ったものを流用しています。

HTMLヘルプAPIには、まだいろいろバグやらインプリメントされていない部分が多数あるので うまく動かない時は、注意が必要です。


[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 13/Sep/2000 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。