Excel VBA+Win32API InputBox呼び出し時にいつも半角入力にしたかったので。

Excel VBAにて、ユーザーになんらかの入力を促すのに便利なInputBoxだが、文字入力時に「常に半角英数入力」のような指定ができないかなあと悩んでいた。

InputBox起動時のIMEの状態は直前までのIMEの状態を引き継ぐので、呼び出し時にIMEがONになっているかOFFになっているかは完全に運になる。

というワケで、今回の問題は勉強を兼ねてWin32APIを使って解決してみることにした。

まあ、ユーザーフォームを使えばお手軽なのは判ってはいたけど(Textbox.IMEmode=fmIMEModeOffを指定すれば良い)、たまには無駄足も踏んでみたいじゃないの。


さて。

VBAでWin32APIを使う場合は、標準モジュールでVisual Basic同様のDeclare文を宣言すれば良い。
Declare文は「[関数名] Lib [DLL名] Alias [エイリアス名][(引数定義)]」という順で記述する。最初は個々の用語の意味がわからずメチャクチャ苦悩する(した)けど、慣れてしまえば簡単。
ちなみに「エイリアス名」っていうのは呼び出したいAPI関数の本来の名前のことで、これを宣言すればDeclare文アタマの[関数名]で独自の名前が定義できるようになる仕組み。
絶対に宣言しないといけないというモノではないけど、VBAでは使用できない「_」(アンダーライン)から始まる関数なんかを使う際には必須になる。


とおさらいしたところで、今回作ったサンプルを書いておく。

                                    • -

'// 自作サンプル //

'// API関数宣言 //
Declare Function FindWindow Lib "User32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function ImmGetContext Lib "imm32.dll" (ByVal hwnd As Long) As Long
Public Declare Function ImmGetOpenStatus Lib "imm32.dll" (ByVal himc As Long) As Long
Public Declare Function ImmReleaseContext Lib "imm32.dll" (ByVal hwnd As Long, ByVal himc As Long) As Long

'// IMEの状態を取得する関数 IsIMEの定義 //
Function IsIME() As Boolean
 Dim hwnd As Long
 Dim himc As Long
 Dim lngRet As Long

 hwnd = FindWindow("XLMAIN", Application.Caption)
 himc = ImmGetContext(hwnd)
 lngRet = ImmGetOpenStatus(himc)
 Call ImmReleaseContext(hwnd, himc)

 Select Case lngRet
  Case 0: IsIME = False
  Case 1: IsIME = True
 End Select
End Function

'// 使用例 //
Sub test()
 '// IMEがONになっていたらOFFにする //
 If IsIME Then SendKeys ("{kanji}")
 imeTest = InputBox("テスト!")
 MsgBox imeTest
End Sub

                                    • -

IMEの状態取得するコードについてはこちらのコードを使用させていただいた。

[Visual Basic Tips IMEの状態取得]
http://www.geocities.co.jp/siliconvalley/4805/vbtips/vbtips076.htm

VBAではウィンドウハンドルを取得する方法が無いため「そっくりこのまま」では使えないので、さらにFindWindow関数でハンドルを得ている部分が、一工夫したつもり。
FindWindow関数で得られるのは「zオーダーの一番高い = アクティブになっているExcel」なので、Excelを多重起動している場合でも問題なくイケる。はず。と思う。
ちなみにこの関数のみAlias文を使っているのは、本来の「FindWindowA」だとケツの「A」がなんかダサいので、という理由だ。


……とせっかく作ったサンプルだけど、しかし本来の業務じゃあ出番ないかな、とは思う。仕事なら面倒なことはせずに最初からユーザーフォームを使うし。
けど、せっかく作ったのでとりあえず残しておく。


Win32APIの使い方がぼんやりと判ってきた。今後はもっと色々便利なことが出来るといいなあ。