Written in Japanese(UTF-8)
2006. 3. 9
INASOFT
/トップ/ユーティリティとゲーム/キーボードシミュレータ/作ってみよう

キーボードシミュレータ / keybd_event APIについて / C++ / C# / VB.NET / J# / C++.NET

Icon キーボードシミュレータを作ってみよう!!


キーボードシミュレータの簡単なコマンドライン版を作ってみようという、プログラミング解説講座です。
第4回では、Visual Basic .NETによる.NET Frameworkなアプリケーションとして作ってみます。

第4回

第3回へ←   →第5回へ


■まずはソースコード

今回は、Visual Basic .NETによる.NET Framework用のプログラムの作成を行います。

Basicとか、Visual Basicとか聞くと、初心者用だなぁとか、プログラムを書きやすいなぁという印象があると思うのですが、今回のプログラミングをしてみて感じたのはこんな感じ。

それでもまぁ、色々こねくり回せば、システムに近い開発をできなくもないですし、文字列に対してFor Eachが使えるあたりは、むしろC++より使いやすいと感じられるところもあるので、ケースバイケースといったところでしょうか。

さて、前回のC#の時と同様に、keybk_event()(Windows API)を使うための特別なテクニックが必要になります。

ソースコードはこんな感じになります。

  ●ソースコードと実行プログラムのダウンロードはこちらから (7KB)



'
' 引数で指定された文字列に従い、キーボード入力をシミュレートします。

Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading

Module keysimvb

    ' Win32APIを呼び出すためのクラス
    Class win32api
        Public Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal _
           bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
    End Class

    Class keysimvb
        Shared Function Main(ByVal args() As String) As Integer
            ' 入力対象のプログラムをアクティブにするまでの待ち時間(ミリ秒単位)
            Dim iSleepTime As Integer = 5000
            ' キーの取りこぼしを防ぐための、各入力間の待機時間(ミリ秒単位)
            Dim iWaitTime As Integer = 10
            ' ※1ミリ秒=0.001秒のこと

            If args.Length = 0 Then
                ' 引数が指定されていない場合、使用方法を表示
                Console.WriteLine("キーボード入力をシミュレートしたい文字列を引数に指定してください。")
                Console.WriteLine("")
                Console.WriteLine("keysimvb <文字列> [<待機時間> [<文字間ウェイト>]]")
                Console.WriteLine("")
                Console.WriteLine("文字列に指定可能な文字は、英大文字・数字・スペース・タブです。")
                Console.WriteLine("(英小文字は無視されますのでご注意下さい)")
                Console.WriteLine("スペース・タブを指定する場合は、文字列全体をダブルコーテーションで囲います。")
                Console.WriteLine("待機時間は、入力開始までの待ち時間をミリ秒単位で指定します(1以上)。")
                Console.WriteLine("文字間ウェイトは、入力文字間のウェイト時間をミリ秒単位で指定します(1以上)。")
                Return 1
            End If

            ' 待機時間取得
            If args.Length > 1 Then
                Try
                    iSleepTime = Convert.ToInt32(args(1))
                Catch ex As FormatException
                    Console.WriteLine("待機時間の指定が不正です。ミリ秒単位で1以上の数を指定してください。")
                    Return 1
                End Try
            End If

            ' 文字間ウェイト取得
            If args.Length > 2 Then
                Try
                    iWaitTime = Convert.ToInt32(args(2))
                Catch ex As FormatException
                    Console.WriteLine("文字間ウェイトの指定が不正です。ミリ秒単位で1以上の数を指定してください。")
                    Return 1
                End Try
            End If

            ' 入力対象のプログラムをアクティブにするまでの待ち
            Console.WriteLine("入力対象のプログラムをアクティブにしてください。")

            Console.WriteLine(iSleepTime.ToString() + "ミリ秒間待機します...")
            Thread.Sleep(iSleepTime)
            Console.WriteLine("")
            Console.WriteLine("入力中...")

            ' コマンドラインの文字列を取得する
            Dim p As String = args(0)

            ' 取得された文字列を1文字ずつ検査し、
            ' シミュレート可能文字列であればkeybd_event APIに渡す。
            For Each pi As Char In p
                ' 入力対象の文字列かどうかを調べる
                If checkKey(pi) = True Then
                    ' キーの押し下げをシミュレートする。
                    win32api.keybd_event(AscW(pi), 0, 0, 0)
                    ' キーの解放をシミュレートする。
                    win32api.keybd_event(AscW(pi), 0, 2, 0) 'KEYEVENTF_KEYUP=2

                    ' dwWaitTimeミリ秒間待機する
                    ' (キーの取りこぼしを防ぐため)
                    Thread.Sleep(iWaitTime)
                End If
            Next

            Console.WriteLine("キーボードシミュレートが終了しました。")

            Return 0
        End Function

        ' ********************************************************************
        ' * checkKey                                                         *
        ' *   キーボードシミュレート対象文字列かどうかを調べる。             *
        ' *   引数cが対象ならばtrueを返し、そうでなければfalseを返す。       *
        ' ********************************************************************
        Shared Function checkKey(ByVal c As Char) As Boolean
            If "0" <= c And c <= "9" Then ' 数字ならOK
                Return True
            ElseIf "A" <= c And c <= "Z" Then ' 英大文字ならOK
                Return True
            ElseIf c = " " Then ' スペースならOK
                Return True
            ElseIf c = "\t" Then ' タブならOK
                Return True
            End If

            ' それら以外の場合はNG
            Return False
        End Function

    End Class

End Module


■実行画面

実行画面は次のようになります。

  1. 引数を付けなかった場合

    
    C:\MyProject\keysimvb\release>keysimvb
    キーボード入力をシミュレートしたい文字列を引数に指定してください。
    
    keysimvb <文字列> [<待機時間> [<文字間ウェイト>]]
    
    文字列に指定可能な文字は、英大文字・数字・スペース・タブです。
    (英小文字は無視されますのでご注意下さい)
    スペース・タブを指定する場合は、文字列全体をダブルコーテーションで囲います。
    待機時間は、入力開始までの待ち時間をミリ秒単位で指定します(1以上)。
    文字間ウェイトは、入力文字間のウェイト時間をミリ秒単位で指定します(1以上)。
    
    C:\MyProject\keysimvb\release>
    

  2. 引数を付けた場合

    
    C:\MyProject\keysimvb\release>keysimvb ABCDEFG12345
    入力対象のプログラムをアクティブにしてください。
    5000ミリ秒間待機します...
    入力中...
    キーボードシミュレートが終了しました。
    
    C:\MyProject\keysimcvb\release>
    

「入力対象のプログラムをアクティブにしてください」という表示が出たところで、貼り付け先のウィンドウをアクティブにしてやる必要があります。引数は、英大文字・数字・スペース・タブを指定してください。英小文字は無視されます。

入力対象のプログラムとして、例えばメモ帳にしたい場合は、メモ帳をアクティブにしておくことで、次のような画面になります(CAPSロックがやカナロック等がかっていない場合)。

まぁ、前回以前と同じ動きをしています。



■解説

書いてみてから気づいたのは、前回にC#で書いたコードとほとんど変わらないということです。

モジュール構造・クラス構造・関数構造・ForやIfのブロックの構造というか、階層構造が全く一緒。細かいことを記述している文法が若干違うだけで、大域的な構造は全くといって同じです。これが、.NET Frameworkの構造的特徴ってことになるでしょうか。

前回同様、VB.NETからは直接Windows APIを呼ぶことはできないので、Windows APIを呼ぶための特別な宣言をしています。具体的には次の部分です。



    ' Win32APIを呼び出すためのクラス
    Class win32api
        Public Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal _
           bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
    End Class

呼び出したいAPI keybd_event() は、user32.dllの中にありますので、 Lib "user32" が付いています。後は、VB.NETが持っている型(.NET Frameworkが持っている型?) の名前を使って、APIを表現してやります。

なお、他の言語(特に、C++やJava等のC系列の言語体系)を利用している方にとっては、ByValの後ろの _ (アンダーバー) の存在が不可解かもしれません。これは、命令が「次の行まで続きますよ」という合図です。VB.NETには、文の終端を表す記号(C系列でいうところの ; (セミコロン))が存在せず、行=文となります。従って、1行の中に1文を表現しきれないときは、特別な表現をしてやる必要があるわけです。これ自体は、VB6.0にも存在した文法なので、VB使いのかたにはおなじみですね。

さて、問題のkeybd_event()の呼び出しそのものは、次のようになります。



    ' キーの押し下げをシミュレートする。
    win32api.keybd_event(AscW(pi), 0, 0, 0)
    ' キーの解放をシミュレートする。
    win32api.keybd_event(AscW(pi), 0, 2, 0)

    ' 上記の「2」は 「KEYEVENTF_KEYUP」を意味しています。

■次回は?

次回は、J#を用いて、キーボードシミュレータ(.NET Framework版)のコマンドライン版を作ってみます。

J#を使う人なんて誰もいないだろう………とか思っちゃったりするもんですね。実際、僕も使ったことがなかったりするので、これが初挑戦です。お楽しみに。

第3回へ←   →第5回へ


キーボードシミュレータ / keybd_event APIについて / C++ / C# / VB.NET / J# / C++.NET
/トップ/ユーティリティとゲーム/キーボードシミュレータ/作ってみよう