第278章 IMEの操作 その1


自分で作ったプログラム上からIMEの操作ができると大変便利です。 今回は、IMEのON, OFFとプロパティダイアログと単語登録ダイアログを 出すプログラムを作ってみます。


まず、例によって前準備が必要です。

1.ソースにimm.hをインクルードする 2.プロジェクトにimm32.libを参加させる

imm何とかという関数が多いですがこれは Imput Method Manager の略です。

IMEのON, OFFにはImmSetOpenStatus関数を使います。

BOOL ImmSetOpenStatus( HIMC hIMC, BOOL fOpen );

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

fOpenは、オープン状態を示します。TRUEで開き、FALSEで閉じます。

関数が成功したら0以外の数を返し、失敗すると0を返します。

では、hIMCを取得するにはどうしたらよいのでしょうか。これには ImmGetContext関数を用います。

HIMC ImmGetContext( HWND hWnd );

hWndには、入力コンテキストを取得するウィンドウのハンドルを指定します。

戻り値は入力コンテキストハンドルです。

さて、この関数を使ったら必ずImmReleaseContext関数でコンテキストハンドルを解放します。

BOOL ImmReleaseContext( HWND hWnd, HIMC hIMC );

hWndには、ImmGetContext関数で指定したウィンドウハンドルを指定します。

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

IMEのON, OFF状態を取得するにはImmGetOpenStatus関数を使います。

BOOL WINAPI ImmGetOpenStatus( HIMC hIMC );

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

プロパティダイアログなどを表示するにはImmConfigureIME関数を使います。

BOOL ImmConfigureIME( HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData );

これで、ダイアログを出します。

hKLは、入力ロケール識別子です。 hKLを取得するために、筆者はロケール関係の関数をいろいろ調べてみましたが なかなか見つかりませんでした。ヘルプで「入力ロケール識別子」そのものを 調べてみると「入力ロケール識別子には音声入力コンバータ、 IME、またはその他のあらゆる入力形式が含まれており、キーボードレイアウトよりも広い概念を持ちます。」 とあります。それで、キーボード関係の関数を調べてみるとGetKeyboardLayout関数というのが ありました。IMEとは関係なさそうな関数ですが、入力ロケールを取得するにはよさそうです。(後述)

さて、続きですがhWndは、ダイアログの親ウィンドウのハンドルを指定します。

dwModeは、次の中から1つを選びます。

IME_CONFIG_GENERALプロパティダイアログボックス
IME_CONFIG_REGISTERWORD単語登録用ダイアログボックス
IME_CONFIG_SELECTDICTIONARY辞書選択用ダイアログボックス

lpDataは、dwModeが単語登録用ダイアログの時のみ意味があります。この時 REGISTERWORD 構造体のアドレスを指定します。

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

typedef struct tagREGISTERWORD { LPTSTR lpReading; LPTSTR lpWord; } REGISTERWORD, *PREGISTERWORD;

lpReadingには、読み方を指定します。

lpWordには、登録する語句を指定します。

不要であればどちらもNULLを指定することができます。

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

元になるものはメモ帳みたいな プログラムで、クライアント領域全体にエディットコントロールを張り付けてあります。 IMEをON, OFFするにはエスケープキーを押します。IMEがONの時でIMEに対してエスケープキーが 意味のあるとき(変換文字列を取り消すときなど)はこれを優先します。

また、メニューの「オプション」からIMEのプロパティダイアログボックスを出せるようにします。 さらに、エディットコントロールに選択文字列(黒く反転させた文字列)がある時に メニューから「単語登録」を選択すると反転させた文字列を登録することができるようにします。

// ime01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)...", IDM_END END POPUP "オプション(&O)" BEGIN MENUITEM "IME設定(&D)...", IDM_IMEDLG MENUITEM "単語登録(&R)...", IDM_REGIST END END

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

// ime01.cpp #ifndef STRICT #define STRICT #endif #define ID_EDIT 100 #include <windows.h> #include <imm.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK NewEditProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "ime01"; //ウィンドウクラス WNDPROC OrgEditProc; //元々のエディットコントロールのプロシージャ

imm.hをインクルードするのを忘れないでください。また、imm32.libを プロジェクトに参加させるのも忘れないでください。

エディットコントロールにエスケープキーが押されたことを知るために これをサブクラス化します。もともとのプロシージャを保存するためのグローバル変数 も用意しておきます。サブクラス化については第36章を参照してください。

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; static HWND hEdit; HINSTANCE hInst; static HKL hKl; REGISTERWORD rw; char szBuf[256] = "", *lpszText; HGLOBAL hMem; DWORD dwStart, dwEnd, dwLen; DWORD i; switch (msg) { case WM_CREATE: hInst = (HINSTANCE)(((CREATESTRUCT *)lp)->hInstance); hEdit = CreateWindowEx(0, "Edit", "", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_WANTRETURN, 0, 0, 0, 0, hWnd, (HMENU)ID_EDIT, hInst, NULL); SetFocus(hEdit); OrgEditProc = (WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC); SetWindowLong(hEdit, GWL_WNDPROC, (LONG)NewEditProc); hKl = GetKeyboardLayout(0); break; case WM_SIZE: MoveWindow(hEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_IMEDLG: ImmConfigureIME(hKl, hEdit, IME_CONFIG_GENERAL, NULL); SetFocus(hEdit); break; case IDM_REGIST: SendMessage(hEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd); if (dwStart != dwEnd) { dwLen = SendMessage(hEdit, WM_GETTEXTLENGTH, 0, 0); hMem = GlobalAlloc(GHND, dwLen + 1); if (hMem == NULL) { MessageBox(hWnd, "メモリを確保できません", "Error", MB_OK); return 0; } lpszText = (char *)GlobalLock(hMem); GetWindowText(hEdit, lpszText, dwLen + 1); SendMessage(hEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd); for (i = 0; i <= dwEnd - dwStart - 1; i++) { szBuf[i] = lpszText[dwStart + i]; } GlobalUnlock(hMem); GlobalFree(hMem); } rw.lpReading = NULL; rw.lpWord = szBuf; ImmConfigureIME(hKl, hEdit, IME_CONFIG_REGISTERWORD, &rw); SetFocus(hEdit); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { SetWindowLong(hEdit, GWL_WNDPROC, (LONG)OrgEditProc); DestroyWindow(hEdit); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

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

WM_CREATEメッセージが来たら、エディットコントロールを作ってこれをサブクラス化しておきます。 また、入力ロケール識別子をスタティック変数に取得しておきます。

HKL GetKeyboardLayout( DWORD idThread );

idThreadには、スレッド識別子を指定します。現在のスレッドであれば0を指定します。

戻り値のhKLの上位ワード値にはキーボードのレイアウトに対するデバイスハンドル、 下位ワード値には言語識別子が入ります。

WM_SIZEメッセージが来たら、エディットコントロールの位置大きさを調整して 親のクライアント領域に合わせます。

メニューからIDM_IMEDLGが選択されたら、ImmConfigureIME関数でプロパティダイアログボックスを 出します。ダイアログが去ったあとフォーカスが失われるので、SetFocus関数でエディットコントロールに フォーカスをセットします。

IDM_REGISTが選択されたら、エディットコントロールにEM_GETSELメッセージを送って 選択文字列の位置を取得します。この位置を元に選択された文字列をszBufにコピーします。 この時位置関係が錯覚を起こしやすいので簡単な例を作って実験しながらプログラムを書くと わかりやすいです。選択文字列をREGISTERWORD構造体のlpWordメンバに指定して ImmConfigureIME関数を呼んでいます。

プログラム終了時にはサブクラス化を解除しています。

LRESULT CALLBACK NewEditProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { HIMC hImc; switch (msg) { case WM_CHAR: if (wp == (WPARAM)0x1b) { hImc = ImmGetContext(hWnd); if (ImmGetOpenStatus(hImc)) { ImmSetOpenStatus(hImc, FALSE); } else { ImmSetOpenStatus(hImc, TRUE); } if (ImmReleaseContext(hWnd, hImc) == 0) MessageBox(hWnd, "ImmReleaseContext Error", "Error", MB_OK); return 0; } default: break; } return CallWindowProc(OrgEditProc, hWnd, msg, wp, lp); }

サブクラス化されたエディットコントロールのプロシージャです。

エスケープキーが押されたら、IMEのON, OFFを切り替えています

今回は比較的簡単なプログラムでした。IMEのON, OFFをユーザーの指定したキーで 切り替えられるように工夫してみてください。


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

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