トップ回答者
ImmSetConversionStatus (かそれに変わる方法で) をWindows8/10で使いたい

質問
-
回答
-
コードありがとうございます。こちらでも再現しました。
WM_IME_NOTIFY メッセージを確認すると、WM_KILLFOCUS の時点では IME が既に閉じているような動きになっていました。こうなると、WM_KILLFOCUS で入力モードの取得を行うのが難しいので、入力モードの取得は入力モードが変わった直後、(WM_IME_NOTIFY の IMN_SETCONVERSIONMODE や WM_IME_NOTIFY の IMN_SETOPENSTATUS)で行ったほうがよさそうです。
ただ、手元のプログラムではユーザーが IME を閉じたときと、フォーカス移動の際に IME が閉じたときの区別がつかない・・・。うーんどうしたもんか・・・
- 編集済み kenjinoteMVP 2016年8月25日 6:25
- 回答としてマーク 星 睦美 2016年9月30日 7:31
すべての返信
-
#include <InputScope.h> HMODULE hMSCTF = LoadLibrary(TEXT("msctf.dll")); typedef HRESULT(WINAPI *SETINPUTSCOPE)(IN HWND, IN InputScope); SETINPUTSCOPE pfnSetInputScope = (SETINPUTSCOPE)GetProcAddress(hMSCTF, "SetInputScope"); if (pfnSetInputScope) { (*pfnSetInputScope)(hEdit, IS_KATAKANA_HALFWIDTH); // 半角カナ //(*pfnSetInputScope)(hEdit, IS_KATAKANA_FULLWIDTH); // 全角カナ } FreeLibrary(hMSCTF);
Windows 8以降では上記のようにすることで、半角カナ/全角カナの切り替えができるようです。参考サイト: https://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/ms629025.aspx
- 編集済み kenjinoteMVP 2016年8月23日 22:30
-
ちなみにVisual C++ 2010から実装されたautoとdecltype()を使用するとちょっとだけ簡単に書けます。
#include <InputScope.h> HMODULE hMSCTF = LoadLibrary(TEXT("msctf.dll")); auto setInputScope = (decltype(SetInputScope)*)GetProcAddress(hMSCTF, "SetInputScope"); if (setInputScope) { setInputScope(hEdit, IS_KATAKANA_HALFWIDTH); // 半角カナ //setInputScope(hEdit, IS_KATAKANA_FULLWIDTH); // 全角カナ } FreeLibrary(hMSCTF);
-
#include <InputScope.h> HMODULE hMSCTF = LoadLibrary(TEXT("msctf.dll")); typedef HRESULT(WINAPI *SETINPUTSCOPE)(IN HWND, IN InputScope); SETINPUTSCOPE pfnSetInputScope = (SETINPUTSCOPE)GetProcAddress(hMSCTF, "SetInputScope"); if (pfnSetInputScope) { (*pfnSetInputScope)(hEdit, IS_KATAKANA_HALFWIDTH); // 半角カナ //(*pfnSetInputScope)(hEdit, IS_KATAKANA_FULLWIDTH); // 全角カナ } FreeLibrary(hMSCTF);
Windows 8以降では上記のようにすることで、半角カナ/全角カナの切り替えができるようです。参考サイト: https://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/ms629025.aspx
ありがとうございます。 試してみます。 -
ちなみにVisual C++ 2010から実装されたautoとdecltype()を使用するとちょっとだけ簡単に書けます。
#include <InputScope.h> HMODULE hMSCTF = LoadLibrary(TEXT("msctf.dll")); auto setInputScope = (decltype(SetInputScope)*)GetProcAddress(hMSCTF, "SetInputScope"); if (setInputScope) { setInputScope(hEdit, IS_KATAKANA_HALFWIDTH); // 半角カナ //setInputScope(hEdit, IS_KATAKANA_FULLWIDTH); // 全角カナ } FreeLibrary(hMSCTF);
-
To remove the input scope association, pass IS_DEFAULT to this parameter.
An application must call this method, passing in IS_DEFAULT to the hwnd parameter, to remove the input scope association before the window is destroyed.
等の記述がありますが、これでは足りませんか?
-
To remove the input scope association, pass IS_DEFAULT to this parameter.
An application must call this method, passing in IS_DEFAULT to the hwnd parameter, to remove the input scope association before the window is destroyed.
等の記述がありますが、これでは足りませんか?
ありがとうございます。
やりたい事は 入力領域(CEdit)ごとに設定した入力モードを保持して、再度その入力領域にフォーカスが来たら
保持していた入力モードを復活させます。以前は ImmGetConversionStatus/ImmSetConversionStatus で上手く動いていました。
ImmGetConversionStatus のような InputScope GetInputScope(HWND) があればいいのですがよろしくお願い致します。
-
ImmGetConversionStatus 関数については、Windows 8 以降でも動作する認識ですがどうでしょうか?
CEdit から他にフォーカスが移る直前に ImmGetConversionStatus 関数にて、CEdit の入力モードを保持し、再度フォーカスが来た時に保持しておいた入力モードの IME_CMODE_KATAKANA、IME_CMODE_FULLSHAPE などのフラグをみて、SetInputScope 関数を呼び出すのはいかがでしょうか?
- 編集済み kenjinoteMVP 2016年8月24日 1:45
-
ImmGetConversionStatus 関数については、Windows 8 以降でも動作する認識ですがどうでしょうか?
CEdit から他にフォーカスが移る直前に ImmGetConversionStatus 関数にて、CEdit の入力モードを保持し、再度フォーカスが来た時に保持しておいた入力モードの IME_CMODE_KATAKANA、IME_CMODE_FULLSHAPE などのフラグをみて、SetInputScope 関数を呼び出すのはいかがでしょうか?
ありがとうございます。
丁度その方法で進めていました。 結果が出ましたらご報告致します。
-
ImmGetConversionStatus 関数については、Windows 8 以降でも動作する認識ですがどうでしょうか?
CEdit から他にフォーカスが移る直前に ImmGetConversionStatus 関数にて、CEdit の入力モードを保持し、再度フォーカスが来た時に保持しておいた入力モードの IME_CMODE_KATAKANA、IME_CMODE_FULLSHAPE などのフラグをみて、SetInputScope 関数を呼び出すのはいかがでしょうか?
ありがとうございます。
丁度その方法で進めていました。 結果が出ましたらご報告致します。
この方法で進めていますがなかなか上手く行きませんでした。
setInputScope に
IS_ALPHANUMERIC_HALFWIDTH, IS_HIRAGANA, IS_ALPHANUMERIC_FULLWIDTH, IS_KATAKANA_FULLWIDTH, IS_KATAKANA_HALFWIDTH
などを設定しても
ImmGetConversionStatus は全て ひらがな (conv=H19) を返すようです。setInputScopeでを設定した後、 画面からIMEの入力モードを別のモードに変更しても ImmGet系で得られる情報はsetInputScapeで設定したままのようです。
上手く動かすには、この2点の問題を解消する必要があるのですが、
いい方法がありましたらご教授お願いします。 -
読みづらいので上げなおしました。
SetInputScope とImmGetConversionStatus で進めていますがなかなか上手く行きませんでした。
setInputScope に
IS_ALPHANUMERIC_HALFWIDTH, IS_HIRAGANA, IS_ALPHANUMERIC_FULLWIDTH, IS_KATAKANA_FULLWIDTH, IS_KATAKANA_HALFWIDTH
などを設定しても
ImmGetConversionStatus は全て ひらがな (conv=H19) を返すようです。setInputScopeでを設定した後、 画面からIMEの入力モードを別のモードに変更しても ImmGet系で得られる情報はsetInputScapeで設定したままのようです。
上手く動かすには、この2点の問題を解消する必要があるのですが、
いい方法がありましたらご教授お願いします。 -
ちょっと調べて分かったことです。
SetInputScope 関数を使用すると、指定されたコントロールに入力モードが覚えられ、ユーザーが入力モードを変更しても次回のコントロールのフォーカス時にSetInputScope 関数で指定した入力モードに戻ってしまうようです。これを解除するには、SetInputMode(hEdit, IS_DEFAULT);というようにIS_DEFAULTを指定します。
ImmGetConversionStatus 関数は、ImmGetContext 関数で取得した戻り値を指定しますが、ImmGetContext の引数のウィンドウハンドルは無視されるようで、現在のフォーカスコントロールの入力モードの状態が取得されるようです。(これはWindows 7 までは IME モードがスレッド単位で保持されていましたが、Windows 8 以降ではユーザー単位で保持されるように仕様が変更されたためだと思います)
フォーカスがあるコントロールの IME 入力モードは SetInputMode で変更したかユーザーが変更したかどうかによらず私の環境(Win10 x64)では正しく取得できました。
[フォーカスを得たときの処理]
1.内部的保持している入力モードを見て SetInputMode で入力モードを設定する
[フォーカスが失われる直前の処理]
1.フォーカスがあるうちに ImmGetConversionStatus 関数で現在の入力モードを取得
2.SetInputMode で入力モードを変更した場合は, SetInputMode(hEdit, IS_DEFAULT);を呼び出して覚えられている入力モードを解除する。上記のような処理でもう一度、動作をご確認できますでしょうか?
- 編集済み kenjinoteMVP 2016年8月24日 5:43
-
ちょっと調べて分かったことです。
SetInputScope 関数を使用すると、指定されたコントロールに入力モードが覚えられ、ユーザーが入力モードを変更しても次回のコントロールのフォーカス時にSetInputScope 関数で指定した入力モードに戻ってしまうようです。これを解除するには、SetInputMode(hEdit, IS_DEFAULT);というようにIS_DEFAULTを指定します。
ImmGetConversionStatus 関数は、ImmGetContext 関数で取得した戻り値を指定しますが、ImmGetContext の引数のウィンドウハンドルは無視されるようで、現在のフォーカスコントロールの入力モードの状態が取得されるようです。(これはWindows 7 までは IME モードがスレッド単位で保持されていましたが、Windows 8 以降ではユーザー単位で保持されるように仕様が変更されたためだと思います)
フォーカスがあるコントロールの IME 入力モードは SetInputMode で変更したかユーザーが変更したかどうかによらず私の環境(Win10 x64)では正しく取得できました。
[フォーカスを得たときの処理]
1.内部的保持している入力モードを見て SetInputMode で入力モードを設定する
[フォーカスが失われる直前の処理]
1.フォーカスがあるうちに ImmGetConversionStatus 関数で現在の入力モードを取得
2.SetInputMode で入力モードを変更した場合は, SetInputMode(hEdit, IS_DEFAULT);を呼び出して覚えられている入力モードを解除する。上記のような処理でもう一度、動作をご確認できますでしょうか?
-
ちょっと調べて分かったことです。
SetInputScope 関数を使用すると、指定されたコントロールに入力モードが覚えられ、ユーザーが入力モードを変更しても次回のコントロールのフォーカス時にSetInputScope 関数で指定した入力モードに戻ってしまうようです。これを解除するには、SetInputMode(hEdit, IS_DEFAULT);というようにIS_DEFAULTを指定します。
ImmGetConversionStatus 関数は、ImmGetContext 関数で取得した戻り値を指定しますが、ImmGetContext の引数のウィンドウハンドルは無視されるようで、現在のフォーカスコントロールの入力モードの状態が取得されるようです。(これはWindows 7 までは IME モードがスレッド単位で保持されていましたが、Windows 8 以降ではユーザー単位で保持されるように仕様が変更されたためだと思います)
フォーカスがあるコントロールの IME 入力モードは SetInputMode で変更したかユーザーが変更したかどうかによらず私の環境(Win10 x64)では正しく取得できました。
[フォーカスを得たときの処理]
1.内部的保持している入力モードを見て SetInputMode で入力モードを設定する
[フォーカスが失われる直前の処理]
1.フォーカスがあるうちに ImmGetConversionStatus 関数で現在の入力モードを取得
2.SetInputMode で入力モードを変更した場合は, SetInputMode(hEdit, IS_DEFAULT);を呼び出して覚えられている入力モードを解除する。上記のような処理でもう一度、動作をご確認できますでしょうか?
ありがとうございます。 やってみてご報告します。
なんども済みません。 まだ上手く動かないのでもう少し教えてください。>[フォーカスが失われる直前の処理]私の方では CEditのサブクラスのOnKillFocus内で CEdit::OnKillFocusを呼ぶ前に
ImmGetContext -> ImmGetOpenStatus -> ImmGetConversionStatus で入力モードを得ようとしていますが、やはり、画面操作で変更した入力モードが上手く取れません。 (SetInputScopeで変更したモードが返ってきます)なにか間違っている箇所はありますでしょうか ?
>[フォーカスが失われる直前の処理]
>1.フォーカスがあるうちに ImmGetConversionStatus 関数で現在の入力モードを取得
>2.SetInputModeで入力モードを変更した場合は, SetInputMode(hEdit, IS_DEFAULT);を呼び出して入力モードを元に戻す。ソフト起動時に入力モードを変更していますので SetInputModeが必ず使っています。
SetInputMode(hEdit, IS_DEFAULT) を呼び出すタイミングが分からないのですが、どのタイミングで呼べばよいでしょうか?よろしくお願いいたします。
-
簡単なサンプルを作りました。 modeの初期値はIS_ALPHANUMERIC_HALFWIDTHにしてますので
CEditの初めは半角英数で表示されて、画面操作で入力モードをひらがなに変えて CEditのフォーカスを外すと OnKillFocusに来るのですが
この場合 半角英数のままに設定されているようですのでImmGetConversionStatusまで来ないでImmGetOpenStatusでエラーになります。setInputScope を数箇所にいれてみたのですが結果は同じでした。
なにか悪いところをご指摘いただけると大変助かります。void CTestEdit::OnKillFocus(CWnd* pNewWnd) { HIMC hImc = ImmGetContext(m_hWnd); //setInputScope(hEdit, IS_DEFAULT); BOOL bOpen = ::ImmGetOpenStatus(hImc); if ( bOpen ) { DWORD conv = 0, sent = 0; //setInputScope(hEdit, IS_DEFAULT); if ( ::ImmGetConversionStatus(hImc, &conv, &sent) ) { switch (conv) { case 25://全角ひらかな mode = IS_HIRAGANA; break; case 27 //全角カタカナ mode = IS_KATAKANA_FULLWIDTH; break; case 19 //半角カタカナ mode = IS_KATAKANA_HALFWIDTH; break; case 24: //全角英数 mode = IS_ALPHANUMERIC_FULLWIDTH; break; default: mode = IS_ALPHANUMERIC_HALFWIDTH; break; } } else { mode = IS_ALPHANUMERIC_HALFWIDTH; } } else { mode = IS_ALPHANUMERIC_HALFWIDTH; } ::ImmReleaseContext(m_hWnd, hImc); CEdit::OnKillFocus(pNewWnd); } void CTestEdit::OnSetFocus(CWnd* pOldWnd) { CEdit::OnSetFocus(pOldWnd); // TODO : ここにメッセージ ハンドラ コードを追加します。 setInputScope(m_hWnd, mode); }
-
コードありがとうございます。こちらでも再現しました。
WM_IME_NOTIFY メッセージを確認すると、WM_KILLFOCUS の時点では IME が既に閉じているような動きになっていました。こうなると、WM_KILLFOCUS で入力モードの取得を行うのが難しいので、入力モードの取得は入力モードが変わった直後、(WM_IME_NOTIFY の IMN_SETCONVERSIONMODE や WM_IME_NOTIFY の IMN_SETOPENSTATUS)で行ったほうがよさそうです。
ただ、手元のプログラムではユーザーが IME を閉じたときと、フォーカス移動の際に IME が閉じたときの区別がつかない・・・。うーんどうしたもんか・・・
- 編集済み kenjinoteMVP 2016年8月25日 6:25
- 回答としてマーク 星 睦美 2016年9月30日 7:31
-
コードありがとうございます。こちらでも再現しました。
WM_IME_NOTIFY メッセージを確認すると、WM_KILLFOCUS の時点では IME が既に閉じているような動きになっていました。こうなると、WM_KILLFOCUS で入力モードの取得を行うのが難しいので、入力モードの取得は入力モードが変わった直後、(WM_IME_NOTIFY の IMN_SETCONVERSIONMODE や WM_IME_NOTIFY の IMN_SETOPENSTATUS)で行ったほうがよさそうです。
ただ、手元のプログラムではユーザーが IME を閉じたときのメッセージと、フォーカス移動の際に IME が閉じたときの区別がつかない・・・。うーんどうしたもんか・・・
お試し大変ありがとうございました。
InputScopeとImm系の共存はなかなか難しいですね。
エンドユーザーはATOKを入れている場合があるようですので、クライアントの意向でInputScopeがNGになりました。
Windows8/10でもコントロールパネルの「アプリウィンドウごとに異なる入力方式を設定する」をONにするとImm系が使えるようですので、とりえずはプログラムからその値をONにする方針となりました。
みなさま 大変ありがとうございました。
-
Windows8/10でもコントロールパネルの「アプリウィンドウごとに異なる入力方式を設定する」をONにするとImm系が使えるようですので、とりえずはプログラムからその値をONにする方針となりました。
その方針で大丈夫なんでしょうか?
ユーザーの設定を勝手に書き換えるものですので、アプリに対する不満につながりかねないと思うのですが…。
(少数顧客、あるいはそのアプリ専用と言える環境など、困らない場合は読み捨ててください)
あまりいい方法ではないですが、ATOK+IME両方使えるようにするには、現状この方法しか考えられません。
一応影響少なくするためアプリ終了時に、設定を戻すようにしました。