IMEでの未確定文字をボタンにより確定するには

[戻る]
ミミ 2004/04/27(火) 11:16:51
ミミと申します。よろしくお願いします。

ダイアログに、エディットボックスと「確定」ボタンを設け、
IME2000で日本語を入力し、変換する前の未確定文字列を
「確定」ボタンにより確定させたいと思っています。
(Enterキーを押せば早い話なのですが、「確定」ボタンも同じ処理をさせたいのです)

「確定」ボタンを押下した時、ダイアログに対して、
     PostMessage(m_hWnd,WM_IME_KEYDOWN, (WPARAM)VK_RETURN, 0);
をやってみましたが「確定」ボタンを押した瞬間にエディットボックスから未確定文字列が
失われてしまいます。

メモ帳を開いて日本語を入力(未確定状態)し、同時にIME2000の「IMEパット」を開き、
IMEパットに表示される[Enter]で、メモ帳の日本語が確定される事と同じ事をしたいのです
が、
アドバイスを頂戴できないでしょうか?

環境は、VC6とMFCを使っています。

PATIO 2004/04/27(火) 11:31:28
ボタンを押してしまうとフォーカスがボタンに移ってしまうと
思います。この場合、入力途中の文字は一旦消えてしまうと
思います。入力していたエディットボックスにフォーカスを戻して
keybd_eventを使ってみてはどうでしょうか?
検証はしていないので御自分で確認してみてください。

シャノン 2004/04/27(火) 12:28:02
エディットボックスにフックを引っ掛けて、IME 関係のメッセージをごにょごにょ…で
できそうな気もします。試してませんが。

ミミ 2004/04/27(火) 14:37:48
PATIOさん、シャノンさん、情報ありがとうございます。

とりあえずこれで私が実現したい事はできました。

OnButton1()
{
 m_edcEdit1.SetFocus();           // ※1
 ::keybd_event(VK_RETURN, 0,0,0); // ※2
                                   // ※3
}

※2で文字が確定される為、※3の位置に
UpdateData(TRUE);
MessageBox(m_edEdit1, "入力された内容", MB_OK);
と入れました。
ですが、一瞬メッセージボックスが表示されるだけで
※2までの処理が無効になってしまいます。

※2までの2行だけなら上手くいくという事より、
やはり、一度Windowsのメッセージループに制御を戻さなければならないのでしょうか?


>エディットボックスにフックを引っ掛けて
すみません、「フック」って何ですか?
私にはわかりませんでした。

シャノン 2004/04/27(火) 15:10:37
ウィンドウ(エディットボックスもウィンドウの一種です)は、メッセージというもの
を受け取ることで様々な処理を行います。
このメッセージを処理するルーチンを「ウィンドウ・プロシージャ」と言います。
フックとは(この場合は)「メッセージ・フック」の略で、ウィンドウ・プロシージャ
を自作のものに置き換えて、流れてくるメッセージに対して特殊な処理を行うことで
す。
「ウィンドウのサブクラス化」とも言います。

エディットボックスに文字を入力するときは、エディットボックスのウィンドウ・プロ
シージャに様々なメッセージが飛んできます。
以前、これを利用して、「エディットボックスに入力した文字列のフリガナを取得」と
いうことをしたことがあります。
似たようなことが今回も出来ないかな、と思ったのですが…

PATIO 2004/04/27(火) 16:18:55
>UpdateData(TRUE);
>MessageBox(m_edEdit1, "入力された内容", MB_OK);
>と入れました。
>ですが、一瞬メッセージボックスが表示されるだけで
>※2までの処理が無効になってしまいます。

>※2までの2行だけなら上手くいくという事より、
>やはり、一度Windowsのメッセージループに制御を戻さなければならないのでしょうか?

そうなると思います。
SetFocusにしろ、keybd_eventしろ結局はメッセージキューにメッセージをつんでいる
だけで処理しているわけではないと思います。
UpdateData(TRUE);を行った段階ではまだ確定して無いと思います。
それに途中にMessageBoxを入れてしまうとそちらにフォーカスを奪われてしまうので
うまく行かなくなるのでしょう。多分、keybd_eventはMessageBoxへ取られていると
思います。だから直ぐに消えてしまうわけです。
キーボードイベントを起こして擬似的に操作する方法では困るのであれば、
IMEを直接コントロールする方法を取らないといけないと思います。
Imm系の関数を調べてみれば、何か出てくるかもしれません。

PATIO 2004/04/27(火) 16:27:56
補足。
SetFocusの実装を確認していないのでSendMessageを使っているのか
PostMessageを使っているのかによって挙動が変わりますね。
その辺は確認しないとはっきりいえないです。
keybd_eventについては、OSのメッセージキューにメッセージを積んでいるだけだと
思います。ですから、その時フォーカスがあるウインドウに対してメッセージが
ディパッチされます。
MessegeBoxの場合はまさにそれが起こっています。

今回の処理はボタンを押すことによってIMEがまだ持っている文字列を取得したいと
いう事だと思うので、エディットコントロール側で如何こうしなくても、
IMEから該当する文字列の取り込みが出来れば、エディットコントロールにセットする
事は可能ではないかと思います。
私も詳しく調べていないのでImm系の関数で解決する確証はないのですが、
逆変換をしたりする事も出来たと思うので直接取得する事もできるのではと
考えました。
外しているかもしれませんが、一度調べてみてください。

PATIO 2004/04/27(火) 16:39:15
SetFocusってAPIのSetFocusを呼んでいたのね。
ってことで取り敢えず、SetFocusの件は置いといて。

ダイアログ上のコントロールでフォーカスの移動をする場合は、
CDialog::GotoDlgCtrlを使ったほうが良いです。
ダイアログ内で管理しているカレントコントロールとの対応がずれてしまいます。

ミミ 2004/04/27(火) 17:01:44
[[解決]]
ミミです。
PATIOさん、シャノンさん、詳しくご説明してくださいましてありがとうございます。

>自作のものに置き換えて、流れてくるメッセージに対して特殊な処理を行うこと
フックというのは、MFCでは例えば、WM_INITDIALOG通知時のOnInitDialog()、
WM_TIMER通知時のOnTimer()の事なのですね。勉強になりました。

今回はどうもIME関係のAPIを使わなければダメなのかなと思い、
あれから過去ログなどを調べて以下の様なコーディングにしました。

OnButton1()
{
    HIMC hIMC = ImmGetContext(m_hWnd);

    DWORD cb = 0;
    cb = ::ImmGetCompositionString(hIMC, GCS_COMPREADATTR, NULL, 0);

    ImmReleaseContext(m_hWnd,hIMC);
    
    if(cb>0){
         // 未確定文字がある時に「確定」より一度確定させる
         // (仮変換された状態ではそのまま確定、変換前では無変換確定)
         GetDlgItem(IDC_EDIT1)->SetFocus();
    ::keybd_event(VK_RETURN, 0,0,0);
         return;
    }

    // 未確定文字がない時に「確定」押された時の処理を以降に記述
    MessageBox("未確定文字列はありません","",MB_OK)
        ・
        ・
        ・
}

とりあえず、これで私が意図する動きは実現できたようです。
ただ、cbに関してはDWORD型の構造体みたいで、その詳しい見方がまだよくわかっていません。
  未確定文字がない時→ ==0
  未確定文字がある時→ >0
の様なので、とりあえずは if(cb>0) のような聞き方をしています。

>keybd_eventしろ結局はメッセージキューにメッセージをつんでいるだけ
私はVC++暦がまだ浅いので、どうも普通のDOS時代のBASICの様な考え方をしてしまう悪いクセ
があるみたいです。
API等も「すぐ実行するものだ」という考え方が頭のどこかに残っているみたいですね。
一度Windowsに処理を戻さなければいけないという事を再認識しました。

Imm関係の関数はVC6のMSDNでは全て英語で理解に苦しみましたので、
Webでも探してどうにかここまでたどり着きました。

皆様、本当にありがとうございました。

PATIO 2004/04/27(火) 17:35:23
一点だけ。

GetDlgItem(IDC_EDIT1)->SetFocus();はやめて

GotoDlgCtrl(GetDlgItem(IDC_EDIT1));を使いましょう。

CDialogクラス内の情報との不整合が起こりますので。

ミミ 2004/04/27(火) 18:12:15
ミミです。

PATIOさん>
アドバイスありがとうございます。
GotoDlgCtrl(GetDlgItem(IDC_EDIT1));
に変更しました。

>ダイアログ内で管理しているカレントコントロールとの対応がずれてしまいます。

折角教えていただいたのに、先に私が掲載した際には見落としていました。
申し訳ありません。

今までフォーカスを移動させるには、
 (1)GetDlgItem(IDC_EDIT1)->SetFocus();か、
 (2)CEdit m_edcEdit1; m_edcEdit1.SetFocus()
のいずれかでコーディングしていました。

フォーカスに関しては、以後 GotoDlgCtrl(GetDlgItem(IDC_EDIT1));
にします。

貴重な情報ありがとうございました。

古いスレッドには追加発言できません。