第76章 メモ帳を作る その3


今回は、自作メモ帳に検索機能を付けます。 簡単そうで、ちょっと一筋縄でいかない部分があります。 まずは、今回使うコモンダイアログの説明をします。

HWND FindText( LPFINDREPLACE lpfr );

検索コモンダイアログを表示する関数です。 引数は例によって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_DOWNは、下方向に検索する事を表します。
FR_ENABLEHOOKは、フック関数を使うことを示します。
FR_HIDE*(*にはMATCHCASE, UPDOWN, WHOLEWORDなどがくる)チェクボックスを隠します。
FR_FINDNEXTは、次の検索をする事を表します。
他にもいろいろありますがヘルプで調べてみてください。

lpstrFindWhatは、検索される文字列のポインタです。

lpstrReplaceWithは、置換に使う文字列のポインタです。

wFindWhatLenは、lpstrFindWhatの長さを与えます。

wReplaceWithLenは、lpstrReplaceWithの長さを与えます。

lCustDataは、フック関数に渡すデータ。

lpfnHookは、フック関数のポインタ。

lpTemplateName標準のダイアログボックスの変わりに使うダイアログボックスの 名前。

ということになります。さて、FINDREPLACE構造体を設定して FindText関数を実行すれば検索ダイアログが現れます。 しかし、これだけではダイアログボックスが現れるだけで 機能することはできません。RegisterWindowMessage関数を 使って独自のメッセージを作ります。プロシージャでこのメッセージを 捕まえたら、検索などを実行します。また、LPARAMは、 FINDREPLACE構造体のポインタとなます。RegisterWindowMessage 関数の引数は、FINDMSGSTRINGにします。

検索の実行にはstrstr関数を使います。

char *strstr( const char *string1, const char *string2 );

string1の中にstring2が含まれるかどうかを調べます。見つからなかった 場合はNULLを返します。見つかった場合は、最初に現れた位置への ポインタを返します。

次に、実際の検索で必要になってくるメッセージ類の解説を します。

EM_GETSEL wParam = (WPARAM) (LPDWORD) lpdwStart; // 最初の位置 lParam = (LPARAM) (LPDWORD) lpdwEnd; // 最後の位置

エジットコントロールで、選択状態(黒くなっているところ)の 文字列の先頭の位置が戻り値の下位、最後の位置が上位に来ます。 これらが、65535を越えると戻り値は−1となります。

EM_SETSEL wParam = (WPARAM) (INT) nStart; lParam = (LPARAM) (INT) nEnd;

これは、選択状態の位置を指定します。 もし、画面上にない位置を指定した場合、スクロールして 選択状態の文字列を画面上に表したいのならば EM_SCROLLCARETメッセージを送信します。(32ビット版) また、エジットコントロールにES_NOHIDESELのスタイルが 含まれていないと、黒くなりません。

EM_SCROLLCARET wParam = 0 ; lParam = 0 ;

さて、このくらいの予備知識があれば大丈夫でしょう。 サンプルのプログラムを見てみることにします。

// memo02.cpp #define STRICT #include <windows.h> #include <windowsx.h> // マクロを使うので必要 #include "resource.h" //リソースエジタ使用 #define ID_EDIT 100 // エジットコントロールのID LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int OpenMyFile(HWND); int WriteMyFile(HWND); int OverWriteMyFile(HWND); int MakeNewFile(HWND); int FimdMyWord(HWND); int SearchMyWord(HWND, /*UINT, WPARAM,*/ LPARAM); char szClassName[] = "memo02"; //ウィンドウクラス char szFileName[256]; // オープンするファイル名(パス付き) char szFile[64] = "無題"; // ファイル名 HINSTANCE hInst; // インスタンスハンドル HWND hEdit; // エジットウィンドウのハンドル char szStr[1024 * 64]; char szFindWhat[256]; UINT uFind; FINDREPLACE fr; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!hPrevInst) { if (!InitApp(hCurInst)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow)) { return FALSE; } uFind = RegisterWindowMessage(FINDMSGSTRING); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

いつもとほぼ同じですが、メッセージループにはいる前に RegisterWindowMessage関数を実行していることに注意してください。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASS wc; 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; return (RegisterClass(&wc)); }

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

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // インスタンスハンドルをグローバル変数にコピー hWnd = CreateWindow(szClassName, "猫でもわかるエジタ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInstance, //インスタンスハンドル 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; RECT rc; switch (msg) { case WM_CREATE: GetClientRect(hWnd, &rc); hEdit = CreateWindow( "EDIT", NULL, WS_CHILD | WS_VISIBLE | ES_WANTRETURN | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | ES_AUTOHSCROLL | WS_HSCROLL | ES_NOHIDESEL, 0, 0, rc.right, rc.bottom, hWnd, (HMENU)ID_EDIT, hInst, NULL); SendMessage(hEdit, EM_SETLIMITTEXT, (WPARAM)1024*64, 0); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_OPEN: OpenMyFile(hWnd); break; case IDM_WRITE: WriteMyFile(hWnd); break; case IDM_UWAGAKI: OverWriteMyFile(hWnd); break; case IDM_NEW: MakeNewFile(hWnd); break; case IDM_FIND: FimdMyWord(hWnd); break; } break; case WM_SIZE: GetClientRect(hWnd, &rc); MoveWindow(hEdit, rc.left, rc.top, rc.right, rc.bottom, TRUE); break; case WM_CLOSE: if (SendMessage(hEdit, EM_GETMODIFY, 0, 0) == TRUE) { id = MessageBox(hWnd, "文書が変更されています。保存しますか?", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) WriteMyFile(hWnd); } id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: if (msg == uFind) { SearchMyWord(hWnd, lp); break; } else { return (DefWindowProc(hWnd, msg, wp, lp)); } } return 0L; }

前回までとたいして変わりませんが、WM_COMMANDの中でIDM_FIND を捕まえている点と、defaultのなかでmsgが自分で作ったメッセージ uFindであると検索関数(自作)を呼んでいる点が付け加わっています。

int OpenMyFile(HWND hWnd) { int id; DWORD dwSize = 0L; OPENFILENAME ofn; HANDLE hFile; DWORD dwAccBytes; char szTitle[64], *szTitle_org = "猫でもわかるエジタ[%s]"; if(SendMessage(hEdit, EM_GETMODIFY, 0, 0) == TRUE) { id = MessageBox(hWnd, "文書が変更されています。保存しますか?", "注意!", MB_OKCANCEL); if (id == IDOK) WriteMyFile(hWnd); } memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "text(*.txt)\0*.txt\0All files(*.*)\0*.*\0\0"; ofn.lpstrFile = szFileName; ofn.lpstrFileTitle = szFile; ofn.nMaxFile = MAX_PATH; ofn.nMaxFileTitle = sizeof(szFile); ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; ofn.lpstrDefExt = "txt"; ofn.lpstrTitle = "ファイルオープン!"; if(GetOpenFileName(&ofn) == 0) return -1; hFile = CreateFile(szFileName, GENERIC_READ, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); dwSize = GetFileSize(hFile, NULL); if (dwSize >= 1024*64) { MessageBox(hWnd, "ファイルサイズが大きすぎます", "Too Big File!", MB_OK); CloseHandle(hFile); dwSize = 0L; return -1; } SetFilePointer(hFile, 0, 0, FILE_BEGIN); ReadFile(hFile, szStr, dwSize, &dwAccBytes, NULL); szStr[dwAccBytes] = '\0'; Edit_SetText(hEdit, szStr); wsprintf(szTitle, szTitle_org, szFile); SetWindowText(hWnd, szTitle); CloseHandle(hFile); return 0; }

これは、前回と同じです。

int WriteMyFile(HWND hWnd) { OPENFILENAME ofn; HANDLE hFile; DWORD dwAccBytes; char szTitle[64], *szTitle_org = "猫でもわかるエジタ[%s]"; int nLen; memset(szStr, '\0', sizeof(szStr)); nLen = GetWindowTextLength(hEdit); GetWindowText(hEdit, szStr, nLen + 1); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "text(*.txt)\0*.txt\0All files(*.*)\0*.*\0\0"; ofn.lpstrFile = szFileName; ofn.lpstrFileTitle = szFile; ofn.nFilterIndex = 1; ofn.nMaxFile = sizeof(szFileName); ofn.nMaxFileTitle = sizeof(szFile); ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; ofn.lpstrDefExt = "txt"; ofn.lpstrTitle = "名前を付けて保存する!"; if(GetSaveFileName(&ofn) == 0) return -1; hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WriteFile(hFile, szStr, strlen(szStr) + 1, &dwAccBytes, NULL); wsprintf(szTitle, szTitle_org, szFile); SetWindowText(hWnd, szTitle); if(CloseHandle(hFile) == 0) MessageBox(hWnd, "Error CloseHandle", "Error", MB_OK); SendMessage(hEdit, EM_SETMODIFY, FALSE, 0); return 0; }

これも、前回と同じです。

int OverWriteMyFile(HWND hWnd) { HANDLE hFile; DWORD dwAccBytes; int nLen; memset(szStr, '\0', sizeof(szStr)); nLen = GetWindowTextLength(hEdit); GetWindowText(hEdit, szStr, nLen + 1); hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WriteFile(hFile, szStr, strlen(szStr) + 1, &dwAccBytes, NULL); SendMessage(hEdit, EM_SETMODIFY, FALSE, 0); if (CloseHandle(hFile) == 0) { MessageBox(hWnd, "Error CloseHandle", "Error", MB_OK); return -1; } return 0; } int MakeNewFile(HWND hWnd) { int id; char szTitle[64], *szTitle_org = "猫でもわかるエジタ[%s]"; if (SendMessage(hEdit, EM_GETMODIFY, 0, 0) == TRUE) { id = MessageBox( hWnd, "文書が更新されています。保存しますか?", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) WriteMyFile(hWnd); } wsprintf(szTitle, szTitle_org, "無題"); SetWindowText(hWnd, szTitle); strcpy(szStr, ""); Edit_SetText(hEdit, szStr); return 0; }

この2つの関数も前回作りました。

int FimdMyWord(HWND hWnd) { HWND hFind; memset(&fr, 0, sizeof(FINDREPLACE)); fr.lStructSize = sizeof(FINDREPLACE); fr.hwndOwner = hWnd; fr.lpstrFindWhat = szFindWhat; fr.wFindWhatLen = 256; fr.Flags = FR_HIDEWHOLEWORD | FR_HIDEUPDOWN | FR_HIDEMATCHCASE; hFind = FindText(&fr); return 0; }

この関数では、FINDREPLACE構造体をセットしてダイアログを 呼び出しています。プログラムを簡略化するために ダイアログボックスのいろいろな設定項目をHIDEしています。

int SearchMyWord(HWND hWnd, LPARAM lp) { int id; LPFINDREPLACE lpfr; static DWORD dwPos = 0; static DWORD dwCurPos; LPSTR lpFindPoint; static char buf[1024*64-1]; START: lpfr = (LPFINDREPLACE)lp; if (lpfr->Flags & FR_FINDNEXT) { dwCurPos = SendMessage(hEdit, EM_GETSEL, 0, 0); if (LOWORD(dwCurPos) == dwPos && dwPos > 0) dwPos++; else dwPos = LOWORD(dwCurPos); SendMessage(hEdit, WM_GETTEXT, sizeof(buf), (LPARAM)buf); lpFindPoint = strstr(&buf[LOWORD(dwPos)], lpfr->lpstrFindWhat); if (lpFindPoint) { dwPos = lpFindPoint - buf; SendMessage(hEdit, EM_SETSEL, dwPos, strlen(lpfr->lpstrFindWhat)+dwPos); SendMessage(hEdit, EM_SCROLLCARET, 0, 0); } else { id = MessageBox(hWnd, "ファイルの最後まで検索しました。先頭から探しますか?", "Find", MB_YESNO); if (id == IDYES) { dwPos = 0; dwCurPos = 0; SendMessage(hEdit, EM_SETSEL, 0, 0); goto START; } } } return 0; }

最初の説明を読めばだいたいのことはわかると思いますが あまりうまいプログラムではありません。もう少しスマートな プログラムに作り替えてみてください。

なお、今回はリソーススクリプトを示しませんでしたが、 メニューに検索(IDM_FIND)を加えただけなのでいちいち 示さなくても大丈夫ですね。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

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