今回は、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#を使う人なんて誰もいないだろう………とか思っちゃったりするもんですね。実際、僕も使ったことがなかったりするので、これが初挑戦です。お楽しみに。