ベスパリブ

日記・備忘録。バイク買いました。ベスパもってないです。

WM_IME_CHAR、WM_IME_KEYDOWN、WM_IME_KEYUPの挙動を調べた

WinAPIの話です。

VK_DELETE メッセージを送信したら、"."(カンマ)が出力される問題

仮想キーVK_DELETEをSendMessageしたら、なぜか"." (カンマ) が出力されてしまう問題に悩んでいました。

原因は単純で、SendMessage(hWnd, WM_IME_CHAR, VK_DELETE, 0)としていたせいでした。

WM_IME_CHARとは何か。
http://www.geocities.jp/katayama_hirofumi_mz/imehackerz/ja/WM_IME_CHAR.html
によると、第三引数のwParamはUnicode文字の値と書いてあります。

VK_DELETEは0x2eです。
WikipediaUnicodeを調べます。
https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF
なるほど。カンマです。

WM_IME_CHARとは?

WM_IME_CHARの仕様は、

IMEが変換結果の文字を取得するときに、アプリに送られます。ウィンドウは、そのウィンドウプロシージャーを通じてこのメッセージを受け取ります。

らしいですが、ちょっとよくわかりません。

原文のMSDNも読みましたが、やはりちょっとよくわかりません。

よくわかっていないなりの理解なのですが、Unicodeを送信したいときはWM_IME_CHARを使えということでしょう。実際、そういう動作をします。

Call SendMessage(hWnd, WM_IME_CHAR, &H21, 0)  ' !
Call SendMessage(hWnd, WM_IME_CHAR, &H2E, 0)  ' .
Call SendMessage(hWnd, WM_IME_CHAR, &H7E, 0)  ' ~ 

結局、DELETEを送信するには?

やりたいことは「DELETEキーを押したい」と同じです。
ここにきて、"仮想キー"の考えが腑に落ちた気がします。仮想キーは文字通り「仮想的なキーボード」なので、「キーを押して」、「キーを離す」というメッセージを送信するということになります。

Call SendMessage(hWnd, WM_IME_KEYDOWN, VK_DELETE , 0)
Call SendMessage(hWnd, WM_IME_KEYUP, VK_DELETE, 0)

何かがおかしい

Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_BACK, 0)

とすると、"b"と"c"が消えました( VK_BACK はBackSpace)。
WM_IME_KEYDOWN と WM_IME_KEYUPでワンセットかと思いきや、 WM_IME_KEYDOWN と WM_IME_KEYUPで二回VK_BACKを押したことになっているようです(私の環境だけ?)。

ちなみに、「押しっぱなし」がどうなるのかも確かめてみました。

Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0)

"c"が消えただけで、"a"と"b"は残りました。どうやら「押しっぱなし」のようなことにはならないようです。

さらに、VK_LEFT(左キー)の挙動を調べました。

Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0)
' "abcXde"
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0)
' "abcXde"
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0)
' "abcdeX"

この辺、挙動を確認しながらプログラム組まないと、よくわからないバグになりそうです。環境による気もしてきました……。 どこかに良い書籍や情報はないでしょうか。

使い分けとか

キーボードでできることは仮想キーにやらせよう(オライリー・ジャパン風)
逆に、 「ʨ」とかの入力はUnicodeでしかできないと思います(入力できるのか知らないけど。私の環境だと?に変換された)

補足

MSDNを辿っていると、知識として入れておいたほうが良さそうなページを見つけました。
キーボード入力