第281章 IMEの操作 その4


今回は、WM_IME_COMPOSITIONメッセージについてプログラムします。 これは、キーストロークにより編集状態が変化したときにアプリケーションに送られてきます。



LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, // WM_IME_COMPOSITION WPARAM wParam, // DBCS character LPARAM lParam );

wParamは、編集文字列の最新の変化を表しているDBCSキャラクタです。

lParamは、編集文字列または文字がいかに変化したかを特定するインジケーターで次の1つまたはそれ以上の組み合わせです。

GCS_COMPATTR編集文字列のアトリビュートが取得された、または最新化された
GCS_COMPCLAUSE編集文字列の文節情報が取得された、または最新化された
GCS_COMPREADATTR現在の編集のリーディング文字列が取得された、または最新化された
GCS_COMPREADCLAUSE編集文字列のリーディング文字列の文節情報が取得された、または最新化された
GCS_COMPREADSTR現在の編集のリーディング文字列が取得された、または最新化された
GCS_COMPSTR現在の編集文字列が取得された、または最新化された
GCS_CURSORPOS編集文字列のカーソル位置が取得した、または最新化された
GCS_DELTASTART編集文字列の変化の開始位置が取得された、または最新化された
GCS_RESULTCLAUSE確定文字列の文節情報が取得された、または最新化された
GCS_RESULTREADCLAUSEリーディング文字列の文節情報が取得された、または最新化された
GCS_RESULTREADSTRリーディング文字列を取得した、または最新化された
GCS_RESULTSTR確定文字列が取得された、または最新化された

さて、WM_IME_COMPOSITIONメッセージを捕まえたらImmGetCompositionString関数で 編集文字列の情報を取得します。

LONG ImmGetCompositionString( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen );

hIMCは、入力コンテキストのハンドルです。

dwIndexは、取得する情報のインデックスですでに書いたGCS_*です。

取得する情報を格納するバッファのポインタです。

dwBufLenはバッファサイズです。これにはユニコードを含んでいてもよいです。
これをゼロにすると必要なバッファサイズが返されます。

戻り値はバッファにコピーされたバイト数です。

また、IMEの変化をアプリケーションに知らせるWM_IME_NOTIFYメッセージというのもあります。

LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, // WM_IME_NOTIFY WPARAM wParam, LPARAM lParam );

wParamは、コマンドを特定します。次のうちの一つです。

IMN_CHANGECANDIDATEIMEが候補ウィンドウの内容を変えようとしたときに送られてきます。
lParamは候補リストフラグです。それぞれのビットがリストに対応しています。ビット0が最初のリストです。
IMN_CLOSECANDIDATE候補ウィンドウを閉じようとしています。lParamは候補リストフラグです。
IMN_CLOSESTATUSWINDOWステータスウィンドウを閉じようとしています。lParamは使われません。
IMN_GUIDELINEエラーメッセージを表示しようとしています。lParamは使われません。
IMN_OPENCANDIDATE候補ウィンドウを開こうとしています。lParamは候補リストフラグです。
IMN_OPENSTATUSWINDOWステータスウィンドウを作ろうとしています。lParamは使われません。
IMN_SETCANDIDATEPOS候補処理が終了して候補ウィンドウを移動しようとしています。
lParamは候補リストフラグです。
IMN_SETCOMPOSITIONFONT入力コンテキストのフォントが最新化されたときに送られてきます。
lParamは使われません。
IMN_SETCOMPOSITIONWINDOW編集ウィンドウのスタイルや位置が変化したときに送られてきます。
lParamは使われません。
IMN_SETCONVERSIONMODE入力文字列モードが変化したときに送られてきます。lParamは使いません。
IMN_SETOPENSTATUSIMEのON,OFF状態が変化したときに送られます。lParamは使いません。
IMN_SETSENTENCEMODE変換モードが変化したときに送られてきます。lParamは使いません。
IMN_SETSTATUSWINDOWPOSステータスウィンドウの位置が変化したときに送られます。lParamは 使いません。

左のウィンドウで日本語を入力すると、右のウィンドウに刻々とIMEの状態が表示されます。



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

// ime03.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)...", IDM_END END POPUP "IME(&I)" BEGIN MENUITEM "IMEの入り切り(&O)", IDM_ONOFF MENUITEM "コード入力(&C)", IDM_CODE POPUP "入力文字種(&L)" BEGIN MENUITEM "全角ひらかな(&H)", IDM_ZENHIRA MENUITEM "全角カタカナ(&K)", IDM_ZENKATA MENUITEM "半角カタカナ(&T)", IDM_HANKATA MENUITEM "全角英数(&E)", IDM_ZENEISU MENUITEM "半角英数(&S)", IDM_HANEISU END POPUP "変換モード(&M)" BEGIN MENUITEM "連文節(&R)", IDM_RENBUN MENUITEM "複合語(&P)", IDM_PLU MENUITEM "自動(&A)", IDM_AUTO MENUITEM "口語優先(&C)", IDM_CONV MENUITEM "無変換(&N)", IDM_NONCONV END MENUITEM "ダイアログ(&F)", IDM_CONFIG END END

メニューのリソース・スクリプトです。

// ime03.cpp #ifndef STRICT #define STRICT #endif #define ID_IME 100 #define ID_IME2 101 #include <windows.h> #include <windowsx.h> #include <imm.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyEditProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int Err(HWND, char *); BOOL MyScroll(); char szClassName[] = "ime03"; //ウィンドウクラス WNDPROC OrgEditProc; HWND hEdit2;

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

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 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, "猫でもわかるIME", //タイトルバーにこの名前が表示されます 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; HIMC hImc; static HWND hEdit; HINSTANCE hInst; CREATESTRUCT *lpcs; DWORD dwConv, dwSent; HKL hKl; switch (msg) { case WM_CREATE: lpcs = (CREATESTRUCT *)lp; hInst = lpcs->hInstance; hEdit = CreateWindow("edit", "", WS_CHILD | WS_BORDER | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_HSCROLL | WS_VSCROLL, 0, 0, 0, 0, hWnd, (HMENU)ID_IME, hInst, NULL); hEdit2 = CreateWindow("edit", "", WS_CHILD | WS_BORDER | WS_BORDER | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_HSCROLL | WS_VSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hWnd, (HMENU)ID_IME2, hInst, NULL); OrgEditProc = (WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC); SetWindowLong(hEdit, GWL_WNDPROC, (LONG)MyEditProc); SetFocus(hEdit); break; case WM_SIZE: MoveWindow(hEdit, 0, 0, LOWORD(lp) / 2, HIWORD(lp), TRUE); MoveWindow(hEdit2, LOWORD(lp) / 2, 0, LOWORD(lp) / 2, HIWORD(lp), TRUE); break; case WM_COMMAND: if (LOWORD(wp) == IDM_END) { SendMessage(hWnd, WM_CLOSE, 0, 0); return 0; } hImc = ImmGetContext(hEdit); ImmGetConversionStatus(hImc, &dwConv, &dwSent); switch (LOWORD(wp)) { case IDM_CONFIG: hKl = GetKeyboardLayout(0); ImmConfigureIME(hKl, hWnd, IME_CONFIG_GENERAL, NULL); break; case IDM_ONOFF: if (ImmGetOpenStatus(hImc)) { ImmSetOpenStatus(hImc, FALSE); } else { ImmSetOpenStatus(hImc, TRUE); } break; case IDM_CODE: //コード入力 if (ImmSetConversionStatus(hImc, IME_CMODE_CHARCODE, IME_SMODE_NONE) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_ZENHIRA: //全角ひらかな if (ImmSetConversionStatus(hImc, IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE, dwSent) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_ZENKATA: //全角カタカナ if (ImmSetConversionStatus(hImc, IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA, dwSent) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_HANKATA: //半角カタカナ if (ImmSetConversionStatus(hImc, IME_CMODE_NATIVE | IME_CMODE_KATAKANA, dwSent) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_ZENEISU: //全角英数 if (ImmSetConversionStatus(hImc, IME_CMODE_FULLSHAPE, dwSent) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_HANEISU: //半角英数 if (ImmSetConversionStatus(hImc, IME_CMODE_ALPHANUMERIC, dwSent) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_RENBUN: //連文節変換 if (ImmSetConversionStatus(hImc, dwConv, IME_SMODE_PHRASEPREDICT) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_AUTO: //自動変換 if (ImmSetConversionStatus(hImc, dwConv, IME_SMODE_AUTOMATIC) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_PLU: //複合語変換 if (ImmSetConversionStatus(hImc, dwConv, IME_SMODE_PLAURALCLAUSE) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_CONV: //会話優先 if (ImmSetConversionStatus(hImc, dwConv, IME_SMODE_CONVERSATION) == 0) Err(hWnd, "ImmSetConversionStatus"); break; case IDM_NONCONV: //無変換(固定入力) if (ImmSetConversionStatus(hImc, dwConv, IME_SMODE_NONE) == 0) Err(hWnd, "ImmSetConversionStatus"); break; } SetFocus(hEdit); if (ImmReleaseContext(hEdit, hImc) == 0) Err(hEdit, "ImmReleaseConText"); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { SetWindowLong(hEdit, GWL_WNDPROC, (LONG)OrgEditProc); DestroyWindow(hEdit); DestroyWindow(hEdit2); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

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

WM_CREATEメッセージが来たら、エディットコントロールを2つ作ります。左側の コントロール(hEdit)はサブクラス化しておきます。

WM_SIZEメッセージが来たら、各エディットコントロールの大きさ、位置を 調整します。

メニューからメニュー項目が選択されたときの処理は前章とほとんど同じです。

int Err(HWND hWnd, char *szStr) { char szBuf[256]; wsprintf(szBuf, "「%s」エラーです", szStr); MessageBox(hWnd, szBuf, "Error", MB_OK | MB_ICONEXCLAMATION); return 0; }

これも前章と同じです。

LRESULT CALLBACK MyEditProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { char szBuf[1024], szStr[1024], szAll[1024 * 64]; HIMC hImc; switch (msg) { case WM_CHAR: if (wp == (WPARAM)0x1b) { hImc = ImmGetContext(hWnd); if (ImmGetOpenStatus(hImc)) { ImmSetOpenStatus(hImc, FALSE); } else { ImmSetOpenStatus(hImc, TRUE); } } break; case WM_IME_COMPOSITION: hImc = ImmGetContext(hWnd); if (lp & GCS_RESULTSTR) { memset(szBuf, '\0', 1024); ImmGetCompositionString(hImc, GCS_RESULTSTR, szBuf, 1024); wsprintf(szStr, "「%s」を確定しました\r\n", szBuf); Edit_GetText(hEdit2, szAll, 1024 * 64 - 1); strcat(szAll, szStr); Edit_SetText(hEdit2, szAll); MyScroll(); } if (lp & GCS_COMPSTR) { memset(szBuf, '\0', 1024); ImmGetCompositionString(hImc, GCS_COMPSTR, szBuf, 1024); wsprintf(szStr, "編集文字列は「%s」です。\r\n", szBuf); Edit_GetText(hEdit2, szAll, 1024 * 64 - 1); strcat(szAll, szStr); Edit_SetText(hEdit2, szAll); MyScroll(); } ImmReleaseContext(hWnd, hImc); break; case WM_IME_NOTIFY: switch (wp) { case IMN_CHANGECANDIDATE: Edit_GetText(hEdit2, szAll, 1024 * 64 - 1); strcat(szAll, "選択候補が変化しました\r\n"); Edit_SetText(hEdit2, szAll); MyScroll(); break; } break; default: break; } return CallWindowProc(OrgEditProc, hWnd, msg, wp, lp); }

左側のエディットコントロールをサブクラス化したものです。 ユーザーがエスケープキーを押したら、IMEのON,OFFを切り替えます。

IMEからWM_IME_COMPOSITIONが来たら、lpに含まれているGCS_*を調べて、右の エディットコントロールに情報を表示します。このプログラムでは 編集文字列が変化したとき、編集文字列が確定したときに表示を出しています。

WM_IME_NOTIFYではwpを調べて変換候補が変化したとき、その情報を右の エディットコントロールに表示します。

さて、Windows95/98ではEdit_GetTextマクロの最後の引数を64キロ未満にしないと マクロが失敗するので注意してください。(

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

右側のエディットコントロールに常に、最後の情報が見える位置にスクロールさせるための 関数です。

このプログラムで表示した以外の情報についても表示するように作り替えてみてください。


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

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