システムメッセージボックスの書き換え


■[プログラミング][VC++]システムメッセージボックスの書き換え

人力検索はてなで、
「AfxGetMessageBoxなどのメッセージボックスのボタン文字の書き換え」
の質問に私が回答した内容を書きます。

ちょっと長いですが、
「AfxMessageBoxなどのシステムメッセージボックスのボタンを書き換えられる唯一の方法」

をご説明いたしますので、ご精読下さい。

                                                                                                        • -

■状況の説明

AfxMessageBox()やMessageBox()などのシステムメッセージボックス関数は、

システムのAPIから呼び出すメッセージボックスなのですが、

これらの上のボタン表示は、

「IDOK」や「IDCANCEL」といった、

システムで決まっている値に関連付けられており、

普通にやろうとしたのでは変更することができないものです。

しかし、「フック」(ローカルフック)というテクニックを使うこと

によって、変更することが可能です。

フックによって変更した表示のサンプル画像

■フックとは

フックとは、ウィンドウズのシステムから、

アプリケーションウィンドウへと流れるメッセージの流れから、
釣り糸でひっかける(フックする)ように
特定のものを拾い上げて横取りし、
ウィンドウに渡る前にデータを書き換えるなどの何らかの
処理をほどこす手法です。
フックの詳細についてはこちらのリンクにありますが、
詳細な説明なので、まだわからないところは適当に読み飛ばしてください。あくまで今回の問題の具体的な実現方法が主体ですので。

フックのしくみ

フックは一般に高度な技法に分類されますが、今回は

「ローカルフック」といって、

自分自身のスレッドのみを対象とした

(自分のアプリケーションだけに影響がある)フックになります。

システム全体のフックよりも

ずっと簡単ですので、具体的なソースとファイルも用意してありますので大丈夫と思います。)

(システム全体のフックにはDLLが必要になりますが、今回は必要ありません)

                                                                                                      • -

■今回の説明及び、ダウンロード可能なファイル

今回、私が仕様にあわせて作った

サンプルの実行プログラム,
プロジェクトソース(VC++6.0)
を用意しましたので、

こちらのページからダウンロードし、保存・解凍してください。
http://www.yoshino-tech.com/sample/HookedDialog.zip

                                                                                                      • -

■処理の説明:使うもの、用意するもの

今回フックを使ってやることは、

メッセージボックスを呼び出す前に、

「フック関数」をシステムに登録(インストール)する

フック関数の中で、必要なデータの書き換えを行う

フック関数を解除(アンインストール)する

という処理です。

インストールといってもアプリケーションのインストールとは
性質が違うものです。ファイルに書き込んだりはしません。

具体的なソースで用意するもの
今回のフックに必要なものを、グローバルで記述したヘッダファイル

"HookMsgBox.h"の中身のソースは以下のようになります。

//////////////////////////////////////////////////////////////////
// フックハンドル用の変数をグローバルで宣言する。
HHOOK MyHookHandle;	// フックハンドル変数
// OKの文字列と、キャンセルの文字列
// ボタンの文字を変更したい場合はここを変更してください
LPCSTR strOK = "OK(Hook)";			// OKボタンの文字
LPCSTR strCancel = "Cancel(Hook)";	// キャンセルボタンの文字
// プロトタイプ宣言
LRESULT CALLBACK MsgBoxHookProc (int nCode, WPARAM wParam ,LPARAM lParam );
int AfxMessageBoxHooked( LPCSTR message , UINT nType );

////////////////////////////////////////////////////////////////////
//
//		メッセージボックス書き換えのための
//		フック関数(ローカルフック用)
//		※ グローバル関数である必要があります。
//		この関数の中を変えるときは慎重に行って下さい。
//
LRESULT CALLBACK MsgBoxHookProc (int nCode, WPARAM wParam ,LPARAM lParam )
{
	// コード判断
	if (nCode >= 0)
	{
		if ( nCode == HCBT_ACTIVATE)
		{
			// OKボタン(IDOK)の内容を書き換える
			SetDlgItemText( ( HWND )wParam, IDOK, strOK );
			// キャンセルボタン(IDCANCEL)の内容を書き換える
			SetDlgItemText( ( HWND )wParam, IDCANCEL, strCancel );
			HRESULT ret;
			// フック関数をアンインストール(フック解除!)
			ret = UnhookWindowsHookEx ( MyHookHandle );

		}
	}
	// 次のフックに渡す
	return CallNextHookEx ( MyHookHandle, nCode, wParam, lParam);
}
/////////////////////////////////////////////////////////////////////
//
//		AfxMessageBoxHooked(メッセージ, 
//
int AfxMessageBoxHooked( LPCSTR message , UINT nType )
{
	// フック関数(MsgBoxHookProc)をインストールする SetWindowHookEx
	MyHookHandle = SetWindowsHookEx ( WH_CBT, MsgBoxHookProc, NULL, GetCurrentThreadId( ) ); 
	return ( AfxMessageBox(message, nType) ); 
	//return  MessageBox("フックメッセージボックス", "HookedMsgBox",MB_OKCANCEL);

}

■使い方

フック付きメッセージボックスの機能を使いたいファイルで、

ヘッダファイルHookMsgBox.hをインクルードします。

#include "HookMsgBox.h"

そして、使いたいときに、フック付きメッセージボックス関数

AfxMessageBoxHookedを呼び出します。

AfxMessageBoxHooked("フックメッセージボックステスト", MB_OKCANCEL);

■ソースの説明

フックに際し使うAPIは、

フック関数を登録(インストール)する

SetWindowsHookEx()関数、

及び、フックを解除する

 UnhookWindowsHookEx()関数

です。

ソースの中での要素は、

・フック状態を維持するフックハンドル変数
今回 MyHookHandle

・フック処理の内容を記述するフック関数
今回 MsgBoxHookProc()

・フックされたメッセージボックスを呼び出す関数
今回 AfxMessageBoxHooked()

・OKボタンとキャンセルボタンの変更後の内容テキストを定義する定数
今回 strOK,strCancel

というものになります。

・フック付きメッセージボックス関数AfxMessageBoxHooked()では、

本来のAfxMessageBox()関数を内部で呼び出していますが、

その直前に、

SetWindowsHookEx関数で、ボタン表示を書き換えるための

フック関数MsgBoxHookProc()をインストールしています。

・フック関数MsgBoxHookProc()の中では、

メッセージボックスウィンドウが生成されたタイミングで、

「IDOK」,「IDCANCEL」のIDで参照する内容(ボタン表記)を書き換え、

書き換えた後で、

UnhookWindowsHookEx()で

フック関数をアンインストールする処理を行っています。

(これを行わないと、すべてのダイアログのOKボタンと

キャンセルボタンが書き換わったままになってしまいます。)

・このソースからOKボタン、キャンセルボタンの文字を自分で変えるには、

LPCSTR strOK = "OK(Hook)";			// OKボタンの文字
LPCSTR strCancel = "Cancel(Hook)";	// キャンセルボタンの文字

の、="文字"の部分を書き換えてください。

■その他

今回のソースでは、フック関数をいじらなくても
ボタン表示を変更できるようにしています。
もしフック関数内部を独自に編集する場合は、
なるべく他の関数を編集する際よりも注意を払ってください。
(フック関数にバグがあると停止してしまいますので。)
何かわからない点、こうしたい点などあればご相談ください。