今回は、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
実行画面は次のようになります。
C:\MyProject\keysimvb\release>keysimvb キーボード入力をシミュレートしたい文字列を引数に指定してください。 keysimvb <文字列> [<待機時間> [<文字間ウェイト>]] 文字列に指定可能な文字は、英大文字・数字・スペース・タブです。 (英小文字は無視されますのでご注意下さい) スペース・タブを指定する場合は、文字列全体をダブルコーテーションで囲います。 待機時間は、入力開始までの待ち時間をミリ秒単位で指定します(1以上)。 文字間ウェイトは、入力文字間のウェイト時間をミリ秒単位で指定します(1以上)。 C:\MyProject\keysimvb\release>
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#を使う人なんて誰もいないだろう………とか思っちゃったりするもんですね。実際、僕も使ったことがなかったりするので、これが初挑戦です。お楽しみに。