ベスパリブ

プログラミングを主とした日記・備忘録です。ベスパ持ってないです。

Editコントロールが入力可能になるまで待つ

WinAPIの話です。
Editコントロールのハンドルを取得できても、Editコントロールに入力可能というわけではないようです。
このせいで、ハンドル取得した直後に文字列をSendMessageしても、テキストボックスに文字が入っていない場合があります。

しょうがないので、Editコントロールが入力可能になるまで待つ関数を作ります。
方法としては、
1. Editコントロールに適当な文字をSendMessageし、文字数を取得する。
2. 文字数が1文字以上なら、入力可能なので、文字を消して終了
3. 文字数が0ならば、入力できていないので、1.に戻る
です。単純ですね。

Public Declare Function SendMessage4GetText Lib "user32" Alias "SendMessageW" (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal iParam As System.Text.StringBuilder) As Integer
Public Const VK_BACK As Integer = &H8  ' BackSpace

Public Sub Wait4Edit(ByVal hWnd As IntPtr)
    Dim buf As New System.Text.StringBuilder()
    While 1
        buf.Length = 2048
        System.Windows.Forms.Application.DoEvents()
        Call SendMessage(hWnd, WM_IME_CHAR, Asc("a"), 0)  ' 適当な文字を送信する
        If SendMessage4GetText (hWnd, WM_GETTEXT, buf.Length, buf) >= 1 Then  ' 入力できていたら、文字数が1文字以上になる
            Exit While
        End If
        System.Threading.Thread.Sleep(100)
    End While
    ' 適当な文字を送っているので、コントロールに入力されている文字を全部消す
    While 1
        buf.Length = 2048
        System.Windows.Forms.Application.DoEvents()
        Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0)
        Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_BACK, 0)
        If SendMessage4GetText (hWnd, WM_GETTEXT, buf.Length, buf) = 0 Then
            Return
        End If
        System.Threading.Thread.Sleep(100)
    End While
End Sub

引数hWndにはEditコントロールのハンドルを与えます。
与えるハンドル間違えたりすると無限ループから返ってこないことに注意。

なぜWhile文を2つに分けているかというと、最初一つにまとめていたのですが、なぜか入力ボックスに文字が残っていたりした("a"が2回送られる? or VK_BACKが反応しない?)ので、「文字を消す」処理を確実に行わせたかったため二つに分けています。この辺の仕様をまだよくわかっていません。でもまあ、動くしいいか(適当)

追記[20180525]

IsWindowVisible()とIsWindowEnabled()という便利な関数を見つけました。これを使えば良さそうです。

Public Declare Function IsWindowEnabled Lib "user32" Alias "IsWindowEnabled" (ByVal hWnd As IntPtr) As Boolean
Public Declare Function IsWindowVisible Lib "user32" Alias "IsWindowVisible" (ByVal hWnd As IntPtr) As Boolean

Public Sub Wait4Edit(ByVal hWnd As IntPtr)
    ' エディットボックスが入力可能になるまで待つ
    While True
        If (IsWindowEnabled(hWnd) = True) And (IsWindowVisible(hWnd) = True) Then
            Exit While
        End If
        System.Threading.Thread.Sleep(10)
    End While
End Sub