にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

Windows 10 (バージョン2004)のMS-IMEでやまぶきRの「,」等の入力がうまくいかない

症状

なんか最近、たぶん12月のWindows Update以来、Windows 10上のMS-IME (Microsoft IME 日本語) + やまぶきR(ローマ字入力用)で「,」「.」などが上手く入力できない時がある。
設定ファイルで、シングルクォート('~')で囲んだ文字は、文字直接入力としてIMEの未確定文字として入力されるはずであるが、それが未確定文字として入力されなくなった。

具体的には次のような症状を示す。(「,」は設定ファイルで「','」として書かれるものを指す。シングルクォート「'」で囲まれた文字直接入力設定の文字ならなんでもよい)

  • 未確定文字列があるときに「,」を入力しようとすると入力されない。または…
    • 未確定文字列の前か後に確定状態で入力される
    • 未確定文字列が消えて「,」だけが残る
    • 未確定文字列が確定される、など
  • 未確定文字列がない場合に「,」を入力しようすると確定した状態で入力される

この動作はアプリによって様々であるが、「,」がIMEの未確定文字として入力できないのは共通している。
Firefox, Chrome, Explorer, Notepad++, Slack, Discord, メモ帳などで試したが、どのプログラムでも再現した。

ATOKではそうならないという情報を得たので、MS-IMEの問題っぽい。実際、以前はMS-IMEを使っていても問題は起きていなかった。Windows 10 May 2020 Update (バージョン2004)によってMS-IMEが新しいものに置き替わり、これに不具合が残っているようだ。結局、MS-IMEの古いバージョンを使うようにしたら解決した。

ソフトウェアのバージョン

  • OS: Windows 10 バージョン 2004 (OS ビルド 19041.685)
  • Microsoft IME 日本語(バージョン 10.0.19041.1*1?)
    • IMJPTIP.DLL, IMJPAPI.DLL, imjpcus.dll, IMJPRANKER.DLL, imjputyc.dllの更新日付がより新しく、12/17に新しくなっている (バージョン 10.0.19041.662)
  • やまぶきR Ver 1.11.1

ワークアラウンド

とりあえずの回避策として、MS-IMEの設定から「以前のバージョンのMicrosoft IMEを使う」をオンにしたら解消される。(とはいえ、互換用に残してるだけだと思うのでいつまで使えるかは分からないが…)

手順は、

  1. 言語バーのMS-IMEの「あ」または「A」の表示となっている部分を右クリック→「設定(S)」
  2. 「全般」
  3. 「以前のバージョンのMicrosoft IMEを使う」をオンにする

あるいは、

  1. Win+X (または画面左下端のWindowsロゴを右クリック)→「設定(N)」
  2. 「時刻と言語」
  3. 「言語」
  4. (「優先する言語」の下から)「日本語」→「オプション」
  5. (「キーボード」の下から)「Microsoft IME」→「オプション」
  6. 「全般」
  7. 「以前のバージョンのMicrosoft IMEを使う」をオンにする

とする。

もしくは、ATOKGoogle日本語入力などほかのIMEを使うと良さそう。

検証

すべてMS-IME (Microsoft IME 日本語)による。

やまぶきR

上記の通り。省略。

DvorakJ

「,」「.」等、その他Unicode文字として出力するように設定されたものについて、同様の症状が発生する。

紅皿

README.pdfによると「Ver. 0.1.4.2 … Windows 10 May 2020 Updateに対応するため、アプリに出力する文字をすべて半角または制御記号とした。」とあり、Windows 10 May 2020 Update (バージョン2004 (20H1))に問題があることが示唆されている。文字直接入力に相当する機能がないため、問題なく動く。

Win32 API

というか、DvorakJや紅皿が使っているプログラミング言語AutoHotKeyがどうやってUnicode文字列を送ってるかを調べたら、どうやらWin32 APIのSendInput()を使ってるっぽい。やまぶきRも同じかもしれない。

SendInput関数の引数の一つとしてINPUT構造体を与えるが、その内部に設定されるKEYBDINPUT構造体に wScan=ユニコードスカラ値, dwFlags=KEYEVENTF_UNICODE を設定して、その状態でSendInput関数を実行すると、例えば漢字などのUnicode文字が入力できる*2

Python3用のコードを置いておく。コードは、

にあるものに変更を加えたものである。

# https://stackoverflow.com/questions/62189991/how-to-wrap-the-sendinput-function-to-python-using-ctypes
import ctypes
import ctypes.wintypes
import time

KEYEVENTF_UNICODE = 0x4
KEYEVENTF_KEYUP = 0x2
INPUT_KEYBOARD = 1

# not defined by wintypes
ULONG_PTR = ctypes.c_ulong if ctypes.sizeof(ctypes.c_void_p) == 4 else ctypes.c_ulonglong

class KEYBDINPUT(ctypes.Structure):
    _fields_ = [('wVk' ,ctypes.wintypes.WORD),
                ('wScan',ctypes.wintypes.WORD),
                ('dwFlags',ctypes.wintypes.DWORD),
                ('time',ctypes.wintypes.DWORD),
                ('dwExtraInfo',ULONG_PTR)]

class MOUSEINPUT(ctypes.Structure):
    _fields_ = [('dx' ,ctypes.wintypes.LONG),
                ('dy',ctypes.wintypes.LONG),
                ('mouseData',ctypes.wintypes.DWORD),
                ('dwFlags',ctypes.wintypes.DWORD),
                ('time',ctypes.wintypes.DWORD),
                ('dwExtraInfo',ULONG_PTR)]

class HARDWAREINPUT(ctypes.Structure):
    _fields_ = [('uMsg' ,ctypes.wintypes.DWORD),
                ('wParamL',ctypes.wintypes.WORD),
                ('wParamH',ctypes.wintypes.WORD)]

class DUMMYUNIONNAME(ctypes.Union):
    _fields_ = [('mi',MOUSEINPUT),
                ('ki',KEYBDINPUT),
                ('hi',HARDWAREINPUT)] 

class INPUT(ctypes.Structure):
    _anonymous_ = ['u']
    _fields_ = [('type',ctypes.wintypes.DWORD),
                ('u',DUMMYUNIONNAME)]

#print(sizeof(INPUT))

SendInput = ctypes.windll.user32.SendInput
SendInput.argtypes = ctypes.wintypes.UINT,ctypes.POINTER(INPUT),ctypes.c_int
SendInput.restype = ctypes.wintypes.UINT

def send_unicode(s):
    i = INPUT()
    i.type = INPUT_KEYBOARD
    for c in s:
        i.ki = KEYBDINPUT(0,ord(c),KEYEVENTF_UNICODE,0,0)
        SendInput(1,ctypes.byref(i),ctypes.sizeof(INPUT))
        i.ki.dwFlags |= KEYEVENTF_KEYUP
        SendInput(1,ctypes.byref(i),ctypes.sizeof(INPUT))

if __name__ == '__main__':
    time.sleep(3)
    send_unicode('漢')

これを試してみたところ、日本語入力がオンのときにSendInputで入力された「漢」は、MS-IMEでは確定状態になったが、Google日本語入力では未確定状態となった。それっぽい。

*1:C:\Windows\System32\IME\IMEJP\imjpuexc.exe の右クリックメニューからプロパティ→詳細→製品バージョン。関係ないけどWindows 8の時はMS-IMEのバージョンが15.いくつになっていたようで、番号が若返ってる気がする、というかOSバージョンと統一されたっぽい

*2:Unicodeスカラ値を指定するwScanはUnsigned Short (16bit)である。16ビットで表せない基本多言語面(BMP)以外のコード値はどう入力するかというと、サロゲートペアを使うといいらしい。 https://stackoverrun.com/ja/q/6096887 を参照。