none
Vista以降のImmGetCandidateListの取得方法 RRS feed

  • 質問

  • ImmGetCandidateListは WM_IME_SETCONTEXTで

    lParamのISC_SHOWUICANDIDATEWINDOWフラグがリセットされている場合

    に可能なようですがリセット方法がいまいち分かりません。

    以下の箇所でどのようにすればよいでしょうか?(XPではうまく表示されます)

    lParam = IntPtr.Zero; だと他のフラグも消えるためか変換リストは取得できないようでした。

    または return IntPtr.Zero; で CallWindowProc を呼ばないようにしてもだめのようでした。

    IntPtr prevProc;
    WndProc hookProc;

    const int ISC_SHOWUICANDIDATEWINDOW = 0x0001;

    hookProc = new WndProc(ImeControl.HookWndProc);
    prevProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC, (int)Marshal.GetFunctionPointerForDelegate(hookProc));

    private static IntPtr HookWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {

    int wp = wParam.ToInt32();
            int lp = lParam.ToInt32();

    switch (msg)
                {

    case WM_IME_SETCONTEXT:

    if ((lp & ISC_SHOWUICANDIDATEWINDOW) > 0)
                        {
                            lParam = ???
                        }
                        break;

    return CallWindowProc(prevProc, hWnd, msg, wParam, lParam);




    • 編集済み dilmun 2016年5月18日 0:35
    • 移動 星 睦美 2016年5月18日 1:24 .NET Framework から
    2016年5月18日 0:24

回答

  • 検証までしてくださりありがとうございます。

    WindowsVista、Windows7では前回までの方法で問題ありませんでしたがwindows10ではだめでした。(8は持っていません)

    こちらもいろんな情報からやはりlParamの変更のみでリストが取得できるようだったのでいろいろ試していました。

    その結果、参考にしたサンプルにてデフォルトでIMEが関連付けられているWindowを破棄していたのですが

    これが原因でした。

    破棄した場合、IMEの本体?にはCallWindowProcしても届かないようですね。

    WindowsVista、Windows7に限り

    破棄した場合はリストはIMN_CHANGECANDIDATEではなくWM_IME_COMPOSITIONで取得できる謎の動作のようです。

    破棄をやめた場合、Windows10でもlParamの変更のみでいけました。

    前回かいた1~3の問題は変わらずでしたがそうたいした問題ではないのでこれで終了とします。

    ありがとうございました。

    (質問の種別が最初は.Net Framework.NET Frameworkのはずでしたけどいつの間にか変わってる・・・?


    • 編集済み dilmun 2016年5月18日 23:37
    • 回答としてマーク 星 睦美 2016年5月19日 4:45
    2016年5月18日 23:17

すべての返信

  • 無関係かもしれませんが、SetWindowLong()はドキュメントにあるように32bit値しか扱えないため、64bit環境ではポインター値などを正常に扱えません。SetWindowLongPtr()を使う必要があると思います。
    2016年5月18日 1:47
  • あまり詳しくありませんが、

    1.ISC_SHOWUICANDIDATEWINDOWは0x0001ではなく0x00000001ですね。
     自身で定義しなおす必要はないと思います。
    2.ISC_SHOWUICANDIDATEWINDOWはあくまでインデックス0番です。
     全てを意図するならISC_SHOWUIALLCANDIDATEWINDOW(0x0000000F)があります。
    3.従って、LPARAMのインデックス部分をどうこうしたいなら上の値を使って
     ビット演算すべきかもしれません。
    4.その前に、当然LPARAMに何がセットされているか調べた思うのですが
     その結果はどうだったのでしょう。

    質問する場合で、かつ、有用な情報が得たい場合は、
    まず、何がしたい(しているつもりな)のか明示したほうが良いかもしれません。
    あたりまえですが、コードが誤っていると仮定した場合、それから意図を読み取るのは
    一般論としては不可能と予測できるからです。

    2016年5月18日 1:59
  • フラグをクリアするには

    int lp = lParam.ToInt32();
    lp &= ~ISC_SHOWUICANDIDATEWINDOW;
    

    というふうにやります。ただこのままだと、lParam のコピー変数 lp の値を変更しただけですので効果はありません。CallWindowProc に変更した lp の値を渡してあげる必要があります。

    return CallWindowProc(prevProc, hWnd, msg, wParam, new IntPtr(lp));


    2016年5月18日 5:33
  • ご回答ありがとうございます。

    SetWindowLongPtrについてx86コンパイルのためエントリポイントが見つからなくSetWindowLongでいいようです。

    IntPtr.Size == 8 で分岐するサンプルがありましたがIntPtr.Size == 4の分岐に入りました。

    >>1.ISC_SHOWUICANDIDATEWINDOWは0x0001ではなく0x00000001

    について.NETのため?自身で定義しないと参照できないと思うのですがどうなんでしょうか?

    また0x0001は省略しているだけで0x00000001と同値ではないでしょうか?

    int lp = lParam.ToInt32();
    lp &= ~ISC_SHOWUICANDIDATEWINDOW;

    にて試してみました。

    結論から申しますと、この値を渡そうがreturn IntPtr.Zero;で抜けようが変わらないようでした。

    原因はWM_IME_NOTIFYメッセージのif (wp == IMN_OPENCANDIDATE || wp == IMN_CHANGECANDIDATE)

    でImmGetCandidateListを取得しようとしていましたがVsita以降ではこのメッセージが来ないようでした。

    別のタイミングで取得したところ取得できました。

    ただ他の挙動もXPと異なり

    1、変換中にバックスペースキーで未変換に戻らない

    2、変換未確定中にImmNotifyIME(this.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);

    をするとWM_IME_STARTCOMPOSITION、WM_IME_COMPOSITION、WM_IME_ENDCOMPOSITION

    がそれぞれ2回飛んできて2こ表示されてしまった。

    3、2のときImmAssociateContext(this.Handle, IntPtr.Zero);をするとImmAssociateContext(this.Handle, this.hIMC);

    をしてもIME変換モードに戻らなくなり漢字変換できなくなるっぽい

    とりあえず2,3に関してはCPS_COMPLETEをCPS_CANCELにすることで誤魔化せそうですが

    1はどうしたらよいでしょうか?変換中はキーメッセージがWM_KEYDOWNにこないので

    自前でImmNotifyIME(this.hIMC, NI_COMPOSITIONSTR, CPS_REVERT, 0);を

    するタイミングがよく分かりません。

    変換中かつバックスペース時に実行という処理をいつどうすればよいかよろしくお願いします。

    (ただ仕様が変わっただけでこれも無視すればそこまで影響ない気もしますが・・・無理そうならあきらめます。)

    >>原因はWM_IME_NOTIFYメッセージのif (wp == IMN_OPENCANDIDATE || wp == IMN_CHANGECANDIDATE)

    >>でImmGetCandidateListを取得しようとしていましたがVsita以降ではこのメッセージが来ないようでした。

    すみません、これは違ったようでした。別途調査します。


    • 編集済み dilmun 2016年5月18日 12:24
    2016年5月18日 12:06
  • WM_IME_NOTIFYメッセージのif (wp == IMN_OPENCANDIDATE || wp == IMN_CHANGECANDIDATE)

    の時点ではImmGetCandidateListは0でその後にWM_IME_COMPOSITIONが飛んでくるので

    そのときに取得すると取得できるようです。やはりlParamは関係なさそうでした。(とりあえずWindows7では)

    2016年5月18日 12:47
  • 私の環境(Win8.1 x64)では

    IMN_OPENCANDIDATE イベントが来た直後の ImmGetCandidateList の戻り値は確かに 0 となっていました。ただ、IMN_CHANGECANDIDATE イベントの直後では ImmGetCandidateList の戻り値は正しく値を返しました。

    WM_IME_SETCONTEXT で lParam の ISC_SHOWUICANDIDATEWINDOW フラグをリセットしなかった場合は、どこで ImmGetCandidateList を呼ぼうが戻り値は常に 0 となりました。Vista以降では、やはり lParam の変更は必要なのではないでしょうか?

    検証したプログラムは、https://github.com/kenjinote/ImmGetCandidateList/blob/master/Source.cpp に置いています。

    2016年5月18日 15:04
  • 検証までしてくださりありがとうございます。

    WindowsVista、Windows7では前回までの方法で問題ありませんでしたがwindows10ではだめでした。(8は持っていません)

    こちらもいろんな情報からやはりlParamの変更のみでリストが取得できるようだったのでいろいろ試していました。

    その結果、参考にしたサンプルにてデフォルトでIMEが関連付けられているWindowを破棄していたのですが

    これが原因でした。

    破棄した場合、IMEの本体?にはCallWindowProcしても届かないようですね。

    WindowsVista、Windows7に限り

    破棄した場合はリストはIMN_CHANGECANDIDATEではなくWM_IME_COMPOSITIONで取得できる謎の動作のようです。

    破棄をやめた場合、Windows10でもlParamの変更のみでいけました。

    前回かいた1~3の問題は変わらずでしたがそうたいした問題ではないのでこれで終了とします。

    ありがとうございました。

    (質問の種別が最初は.Net Framework.NET Frameworkのはずでしたけどいつの間にか変わってる・・・?


    • 編集済み dilmun 2016年5月18日 23:37
    • 回答としてマーク 星 睦美 2016年5月19日 4:45
    2016年5月18日 23:17
  • ご返信ありがとうございます。

    参考にしたサンプルにてデフォルトでIMEが関連付けられているWindowを破棄していたのですが

    IMEが関連付けられてるWindowを破棄していたのですか。。。?そうなると CallWindowProc が処理されないのは、当たり前のような気もします。

    色々と気になることはありますが、もともとの質問である ImmGetCandidateList の変換候補の取得が Vista 以降でも行えるようになったとのことなのでよかったです。

    すみません。検証のプログラムは慣れているC++で行っていましたが、IMEのメッセージ周りや挙動はフレームワークや言語には大きくは左右されないのかなと思いました。

    サンプル等を使っている場合で、明記できる場合はどのサンプルを使っているか明記したり、処理の流れをできるだけ詳しく教えていただければ、より的確な回答があがる可能性が高くなるかと思います。 よろしくお願いいたします。

    2016年5月19日 2:37
  • フォーラム オペレーターの星 睦美です。dilmun さん、こんにちは。

    今回はご自身で解決されたとのことですね。フォーラムでのユーザーからの情報も参考になりましたら幸いです。

    >(質問の種別が最初は...
    今回は返信がつきやすいと思われるフォーラムにオペレーターが質問を移動させていただきました。フォーラムで役立つ回答をすばやく得るためには、適切なフォーラムで回答者に内容をできるかぎり詳しくつたえることがポイントになります。
    フォーラムのご利用方法(質問の投稿)について
    フォーラムのヘルプ

    それでは今後ともフォーラムをお役立てください。


    フォーラム オペレーター 星 睦美 - MSDN Community Support


    • 編集済み 星 睦美 2016年5月19日 4:43 ヘルプの紹介
    2016年5月19日 4:26