検索と置き換え


検索ダイアログ

メモ帳の「検索」メニューから「検索」を指定すると表示されるダイアログ
これも、Windows API が公開しているコモンダイアログの一つです

検索や置き換えダイアログは、ユーザーが検索したい文字列を入力させます
ただし、実際にその文字列から対象を検索するのはアプリケーションの仕事です
このダイアログは、他のダイアログと違いモードレスダイアログボックスです

検索ダイアログボックスを作成するには FindText() 関数を使います

HWND FindText(LPFINDREPLACE lpfr);

lpfr には、初期化情報を格納した FINDREPLACE 構造体へのポインタを指定します
関数が成功すれば、ダイアログボックスのハンドル、失敗すれば NULL が返ります
アプリケーションは、この戻り値から、ダイアログと通信することができます

FINDREPLACE 構造体は次のように定義されています
typedef struct {    // fr 
    DWORD        lStructSize; 
    HWND         hwndOwner; 
    HINSTANCE    hInstance; 
    DWORD        Flags; 
    LPTSTR        lpstrFindWhat; 
    LPTSTR        lpstrReplaceWith; 
    WORD         wFindWhatLen; 
    WORD         wReplaceWithLen; 
    LPARAM        lCustData; 
    LPFRHOOKPROC lpfnHook; 
    LPCTSTR       lpTemplateName; 
} FINDREPLACE;
lStructSize には、この構造体のサイズを指定します
hwndOwner は、ダイアログの親ウィンドウを指定します
このメンバには、必ず有効なウィンドウのハンドルを指定しなければなりません

hInstance は、ダイアログテンプレートを用いる時
テンプレートが格納されている有効なモジュールのインスタンスハンドルを指定します

Flags には、ダイアログボックスの初期化フラグを指定します
このメンバには、以下の定数の組み合わせを指定することができます

定数解説
FR_DIALOGTERM ダイアログボックスが閉じていることを示します
このフラグが指定されている場合
FindText() の戻り値は無効なウィンドウハンドルです
FR_DOWN 文書内の検索文字方向を指定します
このフラグが指定されている場合、文末に向かっての検索を要求しています
FR_ENABLEHOOK lpfnHook で指定されたフック関数を有効にする
FR_ENABLETEMPLATE hInstance が lpTemplateName メンバで指定された
ダイアログテンプレートを含むリソースのインスタンスであることを示す
FR_ENABLETEMPLATEHANDLE hInstance メンバがロード済みのダイアログボックステンプレートを含む
メモリブロックを指していることを表す
このフラグが指定されている場合、lpTemplateName は無視される
FR_FINDNEXT lpstrFindWhat で指定された文字列の
次の出現位置までを検索することを示す
FR_HIDEUPDOWN 「検索する方向」を隠す
FR_HIDEMATCHCASE 「大文字と小文字を区別する」を隠す
FR_HIDEWHOLEWORD 「単語単位で探す」を隠す
FR_MATCHCASE 大文字と小文字を区別することを表す
FR_NOMATCHCASE 大文字と小文字を区別しないことを表す
FR_NOUPDOWN 検索方向ボタンを無効にする
FR_NOWHOLEWORD 単語単位で探さないことを表す
FR_REPLACE lpstrFindWhat で指定された文字列の次の検索結果を
lpstrReplaceWith で指定された文字列と置き換えることを示す
FR_REPLACEALL lpstrFindWhat で指定された文字列の全て検索結果を
lpstrReplaceWith で指定された文字列と置き換えることを示す
FR_SHOWHELP 「ヘルプ」ボタンを表示する
FR_WHOLEWORD 単語単位で探すこと表す

lpstrFindWhat には、検索対象の文字列が格納されるバッファへのポインタを指定します
このバッファは、少なくとも 80 文字以上 を格納できなければなりません
ダイアログを表示する前は、このバッファの文字列でダイアログが初期化され
検索が要求された場合は、このバッファにユーザーが入力した文字列が格納されます

lpstrReplaceWith には、置き換え操作に使われる、置き換える文字列へのポインタです
このメンバは、置き換えダイアログに用いるもので、FindText() はこれを無視します
このバッファも、80 文字以上を格納できる領域が最低でも必要です

wFindWhatLen には、lpstrFindWhat メンバが指すバッファのバイト単位の長さを
wReplaceWithLen には、lpstrReplaceWith メンバが指すバッファの長さをそれぞれ指定します

lCustData は、フック関数に渡すアプリケーション定義の追加情報を
lpfnHook には、フック関数へのポインタを指定します
lpTemplateName には、ダイアログテンプレートの識別子を指定します
これらは、もうお馴染みのメンバですね

くどいようですが、検索ダイアログボックスはモードレスです
これまでのコモンダイアログボックスは、モーダルダイアログボックスで
ダイアログが閉じられれば、構造体に必要な情報が格納されていることが保証されていました
この場合は、親ウィンドウはどのように、ユーザーが検索ボタンを押したことを知るのでしょうか

実は、これを解決するためにメッセージを用います
このダイアログは RegisterWindowMessage() 関数を用いて
FINDREPLACE 定数のメッセージ値を親ウィンドウに送信します

どのような要求でこのメッセージが発生したのかは Flags メンバで知ることができます
例えば、ユーザーが単語単位での検索を望んでいるのであれば
FR_MATCHCASE フラグが有効になっているため & 演算子でこれを調べることができます

メッセージには LPARAM パラメータに FINDREPLACE 構造体へのポインタを格納しています
WPARAM と戻り値はありません
#include <windows.h>
#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static TCHAR strFind[80] , strMsg[1024];
	static UINT uMessage;
	static FINDREPLACE fr;

	if (msg == uMessage && !(fr.Flags & FR_DIALOGTERM)) {
		wsprintf(strMsg , 
			TEXT("上から下へ = %s\n")
			TEXT("大文字と小文字の区別 = %s\n")
			TEXT("単語単位で探す = %s\n")
			TEXT("検索文字列 = %s\n") ,

			fr.Flags & FR_DOWN ? TEXT("TRUE") : TEXT("FALSE") ,
			fr.Flags & FR_MATCHCASE ?
				TEXT("TRUE") : TEXT("FALSE") ,
			fr.Flags & FR_WHOLEWORD ?
				TEXT("TRUE") : TEXT("FALSE") ,
			fr.lpstrFindWhat
		);

		MessageBox(hWnd , strMsg , TEXT("検索情報") , MB_OK);
		return 0;
	}

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		uMessage = RegisterWindowMessage(FINDMSGSTRING);

		fr.lStructSize = sizeof (FINDREPLACE);
		fr.hwndOwner = hWnd;
		fr.Flags = FR_MATCHCASE;
		fr.lpstrFindWhat = strFind;
		fr.wFindWhatLen = 80;

		FindText(&fr);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムを実行すると、起動と同時に検索ダイアログが表示されるでしょう
「次を検索」ボタンを押せば、入力された文字情報などをメッセージダイアログが表示します

もちろん、ユーザーが入力した内容を元に
何を、どのように検索するかは、あなたの手腕にかかっているということになります


置換えダイアログ

Windows のワードパッドには、文字列を置き換える「置換えダイアログボックス」があります
用いる構造体は FINDREPLACE 構造体なので、検索ダイアログボックスと同じです

このダイアログボックスは、ユーザーにテキストエディタに入力されている文字列のうち
指定した文字列を検索し、一致した文字列を指定した置換え文字に変換する機能を提供します
もちろん、このダイアログも入力を助けるだけで
実際に検索したりするのは、アプリケーションの仕事です

置換えダイアログボックスを表示するには ReplaceText() 関数を使います

HWND ReplaceText(LPFINDREPLACE lpfr);

lpfr には、初期化情報を格納した FINDREPLACE 構造体を指定します
関数が成功すれば、ダイアログのハンドルが、失敗すれば NULL が返ります

検索ダイアログと異なり FINDREPLACE 構造体には
lpstrReplaceWith と wReplaceWithLen メンバも設定しておく必要があります
#include <windows.h>
#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static TCHAR strFind[80] ,strRep[80] , strMsg[1024];
	static UINT uMessage;
	static FINDREPLACE fr;

	if (msg == uMessage && !(fr.Flags & FR_DIALOGTERM)) {
		wsprintf(strMsg , 
			TEXT("要求 = %s\n")
			TEXT("大文字と小文字の区別 = %s\n")
			TEXT("単語単位で探す = %s\n")
			TEXT("検索文字列 = %s\n置換え文字列 = %s\n") ,

			fr.Flags & FR_FINDNEXT ? TEXT("次を検索") :
			fr.Flags & FR_REPLACE  ?
				TEXT("置換えして次に") : TEXT("すべて置換え") ,
			fr.Flags & FR_MATCHCASE ?
				TEXT("TRUE") : TEXT("FALSE") ,
			fr.Flags & FR_WHOLEWORD ?
				TEXT("TRUE") : TEXT("FALSE") ,
			fr.lpstrFindWhat , fr.lpstrReplaceWith
		);

		MessageBox(hWnd , strMsg , TEXT("検索情報") , MB_OK);
		return 0;
	}

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		uMessage = RegisterWindowMessage(FINDMSGSTRING);

		fr.lStructSize = sizeof (FINDREPLACE);
		fr.hwndOwner = hWnd;
		fr.Flags = FR_MATCHCASE;
		fr.lpstrFindWhat = strFind;
		fr.lpstrReplaceWith = strRep;
		fr.wReplaceWithLen = fr.wFindWhatLen = 80;

		ReplaceText(&fr);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


検索プログラムを、置換えダイアログを使って書き直したものです
置換えの機能が付属されたこと以外は、全て同じと考えてかまわないでしょう


FindText()

HWND FindText(LPFINDREPLACE lpfr);

検索ダイアログボックスを表示します

lpfr - 初期化情報を格納した FINDREPLACE 構造体へのポインタを指定します

戻り値 - ダイアログボックスのハンドル、失敗すれば NULL

ReplaceText()

HWND ReplaceText(LPFINDREPLACE lpfr);

置換えダイアログボックスを表示します

lpfr - 初期化情報を格納した FINDREPLACE 構造体を指定します

戻り値 - ダイアログのハンドルが、失敗すれば NULL



前のページへ戻る次のページへ