VBAで押されたキーボードを判定する方法【GetAsyncKeyState関数(API)】
VBAマクロで押されたキーボードが何かを取得したい時があると思います。
恐らく、そう思う人の多くは最終的には「Aキーが押されたらこの処理をしたい」「スペースキーが押されたらあの処理をしたい」というように押されたキーが何なのかを取得して、取得したキーに対してそれぞれ処理を振り分けるというマクロを作りたいと考えていると思います。
VBAでのキーボード取得についていろいろ調べてみると、どうやらユーザーフォーム上で使用できる「KeyDown/KeyUp」(リンク先:Microsoftページ)というイベントがあるようですが、ここでやりたいのはユーザーフォームなどは何も使わずただ単に押されたキーを取得するというものです。
というわけで今回はWindows APIの押されているキーボードの状態を取得することのできる「GetAsyncKeyState」という関数を呼び出して、押されたキーボードをVBA上で取得する方法を解説していきます。
今回学ぶことのできる内容は以下のとおりです。
GetAsyncKeyState関数を使って押されたキーを取得する方法
GetAsyncKeyState関数を使ってマウスクリックを取得する方法
GetAsyncKeyState関数とは
GetAsyncKeyState関数とは関数を呼び出した時にキーが押されているかどうか、また前回のGetAsyncKeyState関数呼び出し以降にキーが押されたかどうかを判定することのできる関数です。
押されていないのかを確認することのできる関数ですね!
GetAsyncKeyState関数はVBAで用意されているものではなくWindows API(Application Programming Interface)の中にある関数です。Windows APIとは簡単にいえばWindowsに用意されている機能がまとまっているセットのようなものです。
Windows APIにはGetAsyncKeyState関数以外にも様々な関数があり、VBA上でそれらを呼び出すことで、Windowsに用意されている様々な機能を使用することができます。(たとえばWindowsのシステム効果音を鳴らしたり、指定した秒数だけ処理を止めることができます)
Windows APIをよく知らない方は「なんか難しそう」と感じると思いますがコードの内容はあまり難しいものではないので、VBAがある程度理解できている方であればすぐに理解することができます。
ちなみに名前からわかると思いますが、Windows APIではWindowsの機能を呼び出しているだけので基本的にはWindows以外のOSでは使用することはできません。
GetAsyncKeyState関数の使い方
GetAsyncKeyState関数を使うには、マクロコードのはじめで「Windows APIのGetAsyncKeyState関数を使うよ」と宣言しておく必要があります。
※宣言をしないとGetAsyncKeyState関数は使えずエラーになるので書き忘れに注意しましょう。
宣言時に書く内容は使用しているWindowsが32bitか64bitかによって変わってきます。
以下のどちらかをコードの一番初め(Option Explicitの次の行あたり)に書いておくことで、そのモジュール内でGetAsyncKeyState関数を使うことができるようになります。
Declare PtrSafe Function GetAsyncKeyState Lib “User32.dll” (ByVal vKey As Long) As Integer
Declare Function GetAsyncKeyState Lib “User32.dll” (ByVal vKey As Long) As Integer
※大文字/小文字が違うだけでエラーが発生するので書き間違いに注意
(VBAの構文と違い自動で大文字/小文字を変換してくれません)
上記のどちらのコードを書けばいいかわからない場合は、以下のコードをコピペしてモジュールの最上部(Option Explicitnの下)に書いておきましょう。
1 2 3 4 5 |
#If Win64 Then Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer #Else Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer #End If |
この構文を書いておくことで自動的に使うことのできる方の構文が使用されます。
VBE上では以下のように使えない方の構文が赤色で表示されますが、実行に影響はありません。
キー別の関数作成
GetAsyncKeyState関数の宣言が終わったら次に取得するキー別に関数を作成しておきます。
以下のコードを書くことで任意のキーが押されたいる場合にTrueを返すことができます。
Function 関数名() As Boolean
Const 定数名 = -32768
関数名 = (GetAsyncKeyState(キーコード) And 定数名) = 定数名
End Function
関数名/定数名には好きな関数名/定数名を入力します。
キーコードには取得したいキーのキーコードを入力します。
各キーのキーコードは以下のPDFを参照ください。
『GetAsyncKeyState関数-キーコード一覧(PDFファイル)』
このキーコード一覧の「vb○○」もしくは「数字」がキーコードとなります。
また、上記PDFには記載していませんが、マウス用のキーコードもあります。
左クリックが「vbKeyLButton」、右クリックが「vbKeyLButton」です。
具体例をあげるとCtrlキーを取得したい場合は以下のようなコードで関数を作成します。
(「vbKeyControl」のかわりに「17」と入力しても実行可能)
1 2 3 4 |
Function PressCtrl() As Boolean Const KEY_PRESSED = -32768 PressCtrl = (GetAsyncKeyState(vbKeyControl) And KEY_PRESSED) = KEY_PRESSED End Function |
このコードの場合、[Ctrl]キーが押されている時は「PressCtrl = True」、[Ctrl]キーが押されていない場合は「PressCtrl = False」となります。つまりこの関数でキーが押されているか、押されていないのかを取得しています。
最終的にこの関数をメインのSubプロシージャ内で呼び出すことで、関数内で指定したキー(上記の場合[Ctrl]キー)が押された時に処理を行うという操作が可能になります。
ただ上記のコードの場合、[Ctrl]キー以外のキーを押してもそれらのキーを取得することはできません。そのため面倒ではありますが取得したいキーは全て、この方法で関数として作成しておく必要があります。(※すべてのキーを取得したい場合は、全てのキー分の関数を作成する必要があるということ)
ちなみにこのコードは以下のサイトを参考にしています。なぜ定数に「-32768」を入れているのかなども、わかりやすく解説されているので合わせて読んでみて下さい。
サンプルコード
実際に今までのコードをまとめたサンプルコードを見てみましょう。
以下のコードでは[Ctrl]キーの状態(キーが押されているか、押されていないか)を取得しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Option Explicit #If Win64 Then Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer #Else Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer #End If '---------------------------------------------------------------------------------------------' Function PressCtrl() As Boolean Const KEY_PRESSED = -32768 PressCtrl = (GetAsyncKeyState(vbKeyControl) And KEY_PRESSED) = KEY_PRESSED End Function '---------------------------------------------------------------------------------------------' Sub Main() MsgBox PressCtrl End Sub |
上記のコード(Sub Mainの部分)を実行するとメッセージボックスが表示されます。
機能としてはこのメッセージボックスが表示されるときに[Ctrl]キーが押されている場合は「True」を、押されていない場合は「False」を表示するようになっています。
(マクロの処理速度は手動では追いつけないほどの速さなので、実際は[Ctrl]キーを押したまま実行しないと「True」と表示されないと思いますが)
ループを使って常に入力待ち状態にする
GetAsyncKeyState関数の使い方はわかりましたが、上記のサンプルコードのようにマクロ実行前にキーを押していないといけないというのは少し不便です。できることであれば、好きなタイミングでキーを押したとしてもそのキーを取得できるようにしたいです。
こういった場合はループ文とIf文を使えば再現することができます。
簡単にコードの内容をいうと、常に特に何も処理を行わないループ状態にしておき、キーが押された瞬間(つまりは作成した関数がTrueになった瞬間)に処理を行うという流れを組んでおけば、好きなタイミングでキーを押したとしてもそのキーを取得することができます。
文字だけだとわかりづらいので、実際に以下のコードを実行してみましょう。
実行後に各矢印キーを押してみて下さい。各キーに割り振られたメッセージが表示されます。
またマウスのクリックでも可ですが、マウスの場合は取得タイミングが一瞬で下記のように条件分岐が多くあるとうまく取得できない場合があります。ダブルクリックをすれば確実に取得できるので実際にコードに盛り込んで使う場合は注意が必要です。
※マクロを終了するときは必ず[Esc]キーを押してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
Option Explicit #If Win64 Then Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer #Else Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer #End If '---------------------------------------------------------------------------------------------' Function ESCPress() As Boolean Const KEY_PRESSED = -32768 ESCPress = (GetAsyncKeyState(vbKeyEscape) And KEY_PRESSED) = KEY_PRESSED End Function '---------------------------------------------------------------------------------------------' Function UPPress() As Boolean Const KEY_PRESSED = -32768 UPPress = (GetAsyncKeyState(vbKeyUp) And KEY_PRESSED) = KEY_PRESSED End Function '---------------------------------------------------------------------------------------------' Function DownPress() As Boolean Const KEY_PRESSED = -32768 DownPress = (GetAsyncKeyState(vbKeyDown) And KEY_PRESSED) = KEY_PRESSED End Function '---------------------------------------------------------------------------------------------' Function RightPress() As Boolean Const KEY_PRESSED = -32768 RightPress = (GetAsyncKeyState(vbKeyRight) And KEY_PRESSED) = KEY_PRESSED End Function '---------------------------------------------------------------------------------------------' Function LeftPress() As Boolean Const KEY_PRESSED = -32768 LeftPress = (GetAsyncKeyState(vbKeyLeft) And KEY_PRESSED) = KEY_PRESSED End Function '---------------------------------------------------------------------------------------------' Function MouseLeftPress() As Boolean Const KEY_PRESSED = -32768 MouseLeftPress = (GetAsyncKeyState(vbKeyLButton) And KEY_PRESSED) = KEY_PRESSED End Function '---------------------------------------------------------------------------------------------' Function MouseRightPress() As Boolean Const KEY_PRESSED = -32768 MouseRightPress = (GetAsyncKeyState(vbKeyRButton) And KEY_PRESSED) = KEY_PRESSED End Function '---------------------------------------------------------------------------------------------' Sub Main() MsgBox "キー入力ループ開始" Do If ESCPress = True Then Exit Do 'Escキーが押された時、ループから抜け出す DoEvents 'これが無いとアプリケーションがフリーズ If UPPress = True Then '上矢印キーが押された時は以下の処理を行う MsgBox "上矢印キーが押されました" End If If DownPress = True Then '下矢印キーが押された時は以下の処理を行う MsgBox "下矢印キーが押されました" End If If LeftPress = True Then '左矢印キーが押された時は以下の処理を行う MsgBox "左矢印キーが押されました" End If If RightPress = True Then '右矢印キーが押された時は以下の処理を行う MsgBox "右矢印キーが押されました" End If If MouseLeftPress = True Then '左クリックされた時は以下の処理を行う MsgBox "左クリックされました" End If If MouseRightPress = True Then '右クリックされた時は以下の処理を行う MsgBox "右クリックされました" End If Loop MsgBox "キー入力ループから抜け出しました" End Sub |
実際に実行するとわかるとおり矢印キーを押すたびに、「○矢印キーが押されました」とメッセージが表示されます。(Escキーが押されるまで何回も表示させることができます)
このシステムをうまく利用すれば好きなキーにさまざまな処理を割り振ることができます。
このコードはキーを押さない限り処理を行わないとはいえ、裏で無限ループが常に行われている状態になってしまいます。これはあまりよろしいことではありませんが、どうしてもキー入力待ち状態がほしいという場合に1つの案として参考程度に使ってみて下さい。
またマクロ終了時に入力待ちループから抜け出すことも忘れないよう注意しておきましょう。
使い道はいろいろありますが有効な使い道としては、下記のようなイメージです。
基本的にはループ内で使用する場合がほとんどだと思います。
・処理の長いマクロに対して特定のボタンが押されたら中断もしくは別処理に分岐
・UserFormのCommandButtonの条件分岐(1つのボタンに複数の機能)
通常にボタンクリックの場合は処理①だが、
特定のキーが入力されている状態でボタンクリックすると処理②を実行
まとめ
今回はWindows APIのGetAsyncKeyState関数を使ったキーボードの取得についての内容でした。
キーボードを押したことにするVBAの「SendKeys」というものがありますが、Windows APIを使えばキーボードの押されたキーを取得するということも可能になります。
作業を効率化するマクロに対して、キーボードの入力(文字入力等の操作は除いて)が必要になる機会はほとんどないと思いますが、面白い機能だったので今回紹介させていただきました。
知識として入れておけばいつかは役に立つ時が来るかもしれません。
Windows APIでできる他の操作は「VBA API」で調べれば様々なサイトで解説しているのでそれらも参考にしてみるといいと思います。