変換中の文字列が表示されている領域(コンポジションウィンドウ)を直接取得する方法は見つからなかったが、IMEが使用中のフォントを指定してGetTextExtentPoint32を使えば、編集中の文字列の縦・横のサイズと同じものをピクセルで取得できる。
ただ、コンポジションウィンドウのフォントを取得する方法がない。
取得する方法はないが設定はImmSetCompositionFontでできるので、設定した後であれば使用中のフォントがわかるのでそれを使う。
#pragma warning(disable:4996) #include <windows.h> #include <imm.h> #pragma comment(lib, "imm32.lib") #include <string>
// IMEで使用するフォントを設定 void SetFont(HIMC hIMC,HFONT hFont) { // フォントの情報を取得 LOGFONT logFont = {}; GetObject(hFont, sizeof(LOGFONT), &logFont); // IMEのコンポジションウィンドウにフォントを設定 ImmSetCompositionFont(hIMC, &logFont); }
// 変換中の文字列を取得 std::wstring GetCompositionString(HIMC hIMC) { std::wstring str; if (hIMC) { DWORD dwSize = ImmGetCompositionString(hIMC, GCS_COMPSTR, NULL, 0); if (dwSize > 0) { wchar_t* compStr = new wchar_t[dwSize / sizeof(wchar_t) + 1]; ImmGetCompositionString(hIMC, GCS_COMPSTR, compStr, dwSize); compStr[dwSize / sizeof(wchar_t)] = L'\0'; str = compStr; delete[] compStr; } } return str; }
// 文字列を描画するサイズを取得
SIZE GetTextDrawSize(const std::wstring& str, HWND hWnd, HDC hdc) { // フォントの情報を取得 HFONT hFont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0); // HDCにフォントを選択 HFONT old = (HFONT)SelectObject(hdc, hFont); // 変換中の文字列のサイズを計算 SIZE textSize; GetTextExtentPoint32(hdc, str.data(), str.length(), &textSize); SelectObject(hdc,old); return textSize; }
// 変換中の文字列の描画領域を計算 SIZE CalculateCompositionStringSIZE(HWND hWnd, HIMC hIMC,HDC hdc) { SIZE size = { 0, 0 }; if (hIMC) { std::wstring test = GetCompositionString(hIMC); size = GetTextDrawSize(test,hWnd,hdc); OutputDebugStringW(test.c_str()); } return size; }
// 変換中の文字列 void test_GCS_COMPSTR(HWND hWnd,HIMC hIMC, HDC hdc) { DWORD inputBytes = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0); int inx = 100; int iny = 100; if (inputBytes) { SIZE size = CalculateCompositionStringSIZE(hWnd, hIMC, hdc); int iny2 = iny - 50; // inx ,iny-50 にsizeの矩形を描画 RECT rect; rect.left = inx; rect.top = iny2; rect.right = inx + size.cx; rect.bottom = iny2 + size.cy; Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom); } if (inputBytes) { std::wstring text = GetCompositionString(hIMC); SetBkMode(hdc, TRANSPARENT); // 確定した文字列を描画 TextOutW(hdc, inx, iny, text.data(), text.length()); SetBkMode(hdc, OPAQUE); } }
// GCS_RESULTSTR void test_GCS_RESULTSTR(HIMC hIMC,HDC hdc) { // GCS_RESULTSTR ... IMEの入力が確定されたフラグ // 確定した文字列のバイト数を取得(NULLは含まない) DWORD inputBytes = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); if (inputBytes) { size_t U16Count = inputBytes / sizeof(wchar_t) + 1;// wchar_tでの、NULLを含んだ文字数 wchar_t* buffer = new wchar_t[U16Count]; // 確定した文字列を取得 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buffer, inputBytes); buffer[U16Count - 1] = L'\0'; // 確定した文字列を描画 int inx = 100; int iny = 100; TextOutW(hdc, inx, iny, buffer, wcslen(buffer)); delete[] buffer; } }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HIMC hIMC; int inx = 100; int iny = 100; switch (message) { case WM_CREATE: // IMEを使用可能にする hIMC = ImmAssociateContext(hWnd, ImmCreateContext()); SetFont(hIMC, (HFONT)GetStockObject(DEFAULT_GUI_FONT)); break; case WM_IME_STARTCOMPOSITION: OutputDebugString(L"IME 入力開始\n"); hIMC = ImmGetContext(hWnd); // IMEコンテキストを取得 if (hIMC) { // IMEのウィンドウ位置を設定 COMPOSITIONFORM cf2; cf2.dwStyle = CFS_POINT; cf2.ptCurrentPos.x = inx; cf2.ptCurrentPos.y = iny; ImmSetCompositionWindow(hIMC, &cf2); // IMEのウィンドウ位置を設定 ImmReleaseContext(hWnd, hIMC); } break; case WM_IME_COMPOSITION: // IMEの入力中の処理 hIMC = ImmGetContext(hWnd); if (hIMC) { HDC hdc = GetDC(hWnd); RECT rc{ 0,0,640,480 };// 画面クリア FillRect(hdc, &rc, (HBRUSH)(WHITE_BRUSH)); if (lParam & GCS_COMPSTR) { test_GCS_COMPSTR(hWnd,hIMC, hdc); // 編集中の処理 } if (lParam & GCS_RESULTSTR) { test_GCS_RESULTSTR(hIMC, hdc); // 確定時の処理 } ReleaseDC(hWnd, hdc); ImmReleaseContext(hWnd, hIMC); } break; case WM_IME_ENDCOMPOSITION: OutputDebugString(L"IME 入力終了\n"); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return DefWindowProc(hWnd, message, wParam, lParam);; } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = L"SZL-IME-TEST"; RegisterClass(&wc); HWND hWnd = CreateWindowW(L"SZL-IME-TEST", L"title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); MSG msg = {}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }