vbabeginner.net Open in urlscan Pro
183.181.91.144  Public Scan

URL: http://vbabeginner.net/getasynckeystate/
Submission: On July 15 via manual from JP — Scanned from JP

Form analysis 2 forms found in the DOM

GET https://vbabeginner.net/

<form role="search" method="get" id="searchform" class="searchform" action="https://vbabeginner.net/">
  <input type="text" value="" name="s"><button class="btn-search"><img src="http://vbabeginner.net/wp-content/themes/keni80_wp_standard_all_201901142358/images/icon/search_black.svg" width="18" height="18"></button>
</form>

GET https://vbabeginner.net/

<form role="search" method="get" id="searchform" class="searchform" action="https://vbabeginner.net/">
  <input type="text" value="" name="s"><button class="btn-search"><img src="http://vbabeginner.net/wp-content/themes/keni80_wp_standard_all_201901142358/images/icon/search_black.svg" width="18" height="18"></button>
</form>

Text Content

Excel作業をVBAで効率化

いつものExcel作業はVBAを使えば数秒で終わるかもしれませんよ


 * ホーム
 * お問い合わせ
 * このサイトについて
 * サイトマップ
 * プライバシーポリシー
 * 
   







どのキーが押されたのかをVBAで判定する(GETASYNCKEYSTATE)

 * 公開日:2021年9月12日

 * Win32API

Tweet

0
目次
 1. どのキーが押されたのかはGetAsyncKeyState関数で確認
 2. GetAsyncKeyState関数は無限ループとSleep関数と一緒に使う
 3. 構文
 4. GetAsyncKeyState関数の戻り値の詳しい話
 5. サンプルコード


どのキーが押されたのかはGETASYNCKEYSTATE関数で確認

キーボードのどのキーが押されたのかをVBAで取得するには、Win32APIのGetAsyncKeyState関数を使います。

キーの押し方には、文字入力のために使う単独キー入力の場合と、ショートカットとして複数のキーが同時に押される場合がありますが、いずれの場合もGetAsyncKeyState関数で取得できます。

なお、キーを「押す」場合にはVBAのSendKeysステートメントを使います。






GETASYNCKEYSTATE関数は無限ループとSLEEP関数と一緒に使う

GetAsyncKeyState関数を単独で使うことはまずありません。ほとんどの場合は無限ループとセットになっています。

なぜかと言うと、キーが押されたことを知りたい、ということは、何かしらユーザーのキーボード操作を待っている状態であることが前提になります。ユーザーの操作を待っている、ということは、操作がされるまで待ち続けることになりますが、その「待ち続ける」処理は無限ループで実現するのが一般的です。



例えて言えば、ゲームのキャラクターをコントローラーの十字キーの代わりに矢印の↑→←↓キーで操作している場合などです。ゲームが終わるまでキー操作が行われることを常に監視しなければならないため、結果としてゲームオーバーなどのなんらかの停止条件に達するまで、無限ループをしながらキー操作の判定を行うことになります。

ただし、無限ループをずっと続けることはCPUの占有に繋がるため、Win32APIのSleep関数を使ってCPUを一定時間休止させることも必要です。そのCPUの休止を判定するためにタイマーを使ってどれだけ時間が経過したかを判定することも必要になります。

これらのことから、必然的にGetAsyncKeyState関数を使う場合は、無限ループとSleep関数と時間の経過判定を行うことになります。

もちろんそうでない場合もありますがかなり特殊なケースです。ここではそのような特殊なケースや無理やりのGetAsyncKeyState関数を動かすだけのサンプルコードではなく、一般的な使い方でのサンプルコードを紹介します。






構文

64bit版です。モジュールに以下の構文をそのまま書いておくと利用できます。一般的には標準モジュールの先頭に書くことが多いです。

Declare PtrSafe Function GetAsyncKeyState Lib “user32” (ByVal vKey As Long) As
Integer

vKey 押したことを確認したいキーを指定します。KeyCodeConstants定数を指定します。定数の数値を直接しても構いません。



KeyCodeConstants定数の詳細は「VBAのキーコード一覧」をご参照ください。

戻り値(Integer) 4種類の数値を返します。詳細は後述します。



値 内容 0 キーが押されていない、かつ、前回のGetAsyncKeyState関数呼び出し後にキーが押されていない 1
キーが押されていない、かつ、前回のGetAsyncKeyState関数呼び出し後にキーが押されている(別アプリがGetAsyncKeyState関数を呼び出した可能性あり)
-32768 キーが押されている、かつ、前回のGetAsyncKeyState関数呼び出し後にキーが押されていない -32767
キーが押されている、かつ、前回のGetAsyncKeyState関数呼び出し後にキーが押されている(別アプリがGetAsyncKeyState関数を呼び出した可能性あり)







GETASYNCKEYSTATE関数の戻り値の詳しい話

GetAsyncKeyState関数の戻り値について上では概要だけ書いてますが、少し細かく書きます。

GetAsyncKeyState関数の戻り値はC言語のshort型という2バイト(16bit)のサイズになります。short型は範囲として-32768から32767の値が使えます。VBAでは同じ2バイトのInteger型が使われています。16ビットということは2の16乗の範囲でデータを持つことが出来ます。0から65535の範囲ですが、Integer型はマイナス値も含めるため-32768から32767の範囲になります。

そして、ここが一番特殊な話ですが、GetAsyncKeyState関数の戻り値は、Integer型を2進数として扱った場合の一番左(最上位)の値が1であればキーが押されている、0であれば押されていない、と判定し、一番右(最下位)の値が1であれば前回GetAsyncKeyState関数を呼び出したあとにキーが押されている、0であれば押されていない、と判定します。

整理すると以下のようになります。

2進数 10進数 16進数 キーが押されているか? 前回のGetAsyncKeyState関数呼び出し後に押された? 0000 0000 0000 0000 0
&H0 押されていない 押されていない 0000 0000 0000 0001 1 &H1 押されていない 押された(別アプリが呼び出した可能性あり) 1000
0000 0000 0000 -32768 &H8001 押されている 押されていない 1000 0000 0000 0001 -32767 &H8000
押されている 押された(別アプリが呼び出した可能性あり)

戻り値の-32768と-32767がなんでマイナスの値なのかですが、最上位ビットに1が設定されていることが原因です。通常使う10進数のマイナスを表す場合は-記号を使うルールですが、2進数の場合はマイナス記号(負号)を使わずに一番左側にある最上位ビットが1の場合はマイナスとして扱うルールになっています。そのため、マイナス値の戻り値になります。詳しいことを知りたい場合は「2の補数」を調べてみてください。

このように2進数の各ビット値の1と0でなんらかの状況のONとOFFを表現するものがWin32APIではよくあります。

最下位ビットの判定は注意が必要です。「最下位ビットが1だから、キーが押されたことを初めて検知した」と判断したくなりますが、それは誤検知の恐れがあります。他のアプリケーションでGetAsyncKeyState関数を利用している場合はそれがOS全体に引き継がれるため、その場合には正しく判定できません。

そのため、MSDNでは「最下位ビットの動作は信頼しないでください」という趣旨のことが書いてあります。どのキーが押されたかではなく、キーが押されたかどうかを優先して判定したいのであればGetAsyncKeyState関数を使うのではなく、フォームでのKeyDownイベントやKeyPressイベントを使うことをお勧めします。






サンプルコード

以下のコードはGetAsyncKeyState関数を使う際の無限ループの仕組みとSleep関数を使ったCPU占有の解放も考慮しています。

Win32APIを3つ使っています。GetAsyncKeyState関数、Sleep関数、GetTickCount関数です。Sleep関数は引数の値をミリ秒スリープします。GetTickCount関数はPCが起動してからの経過時間をミリ秒単位で返します。

Sleep関数についての詳細は「VBAの処理を一時停止する(Sleep、Wait)」をご参照ください。

VBAの関数が2つありますが、GetAsyncKeyStateTest関数が実行する側のメイン関数で、ChkKeyPush関数がGetAsyncKeyState関数を使って指定したキーが押されたかどうかを判定します。

1つ目のGetAsyncKeyStateTest関数はAキーとShiftキーが押されたときにイミディエイトウィンドウに”A”、”Shift”を出力し、Qキーが押されたときに”Quit”と出力して無限ループを終了して関数を終了します。無限ループ+スリープ+DoEventsでのWindows制御可を実装しているため一般的な常時稼働中でのキーが押されたことの判定処理として利用できます。

2つ目のChkKeyPush関数がGetAsyncKeyState関数を実際に利用している箇所です。&H8000で最上位ビットが1かどうかの判定も入れてます。最上位ビットを判定しない場合、レアケースではありますが他アプリケーションで指定キーと同じキーをGetAsyncKeyState関数で判定している場合に、戻り値の最下位ビットに1が設定され、「キーが初めて押された=True」と誤検知する恐れがあるためそれを防いでいます。


Visual Basic

Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Declare
PtrSafe Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long Sub
GetAsyncKeyStateTest() Dim bEndFlg As Boolean '//
ループ終了フラグ(True:ループ継続、False:ループ終了) Dim lStartTimer As Long '// 基点時刻 Dim sMsg As
String '// 出力MSG '// ループ継続 bEndFlg = True '// 基点時刻を取得 lStartTimer = GetTickCount
Do '// ループ終了フラグが「ループ終了」の場合 If bEndFlg = False Then '// ループを抜ける Exit Do End If
Dim bKeyA As Boolean Dim bKeyShift As Boolean sMsg = "" '// Qが押された場合 If
(ChkKeyPush(vbKeyQ) = True) Then '// ループ終了フラグを「ループ終了」に更新 bEndFlg = False
Debug.Print "Quit" End If '// Aが押された場合 If (ChkKeyPush(vbKeyA) = True) Then bKeyA
= True Else bKeyA = False End If '// Shiftが押された場合 If (ChkKeyPush(vbKeyShift) =
True) Then bKeyShift = True Else bKeyShift = False End If '// Aが押されていた場合 If
bKeyA = True Then sMsg = "A " End If '// Shiftが押されていた場合 If bKeyShift = True Then
sMsg = sMsg & "Shift " End If If sMsg <> "" Then Debug.Print sMsg End If '//
Windowsに制御を渡す DoEvents '// 0.1秒経過するまでスリープ Do While GetTickCount - lStartTimer <
100 '// CPUを休ませる(ループ処理にCPUが占有されないようにして負荷を下げる) Call Sleep(1) Loop '// 基点時刻を再取得
lStartTimer = GetTickCount Loop End Sub Function ChkKeyPush(a_iKeyCode) '//
指定キーが押された If (GetAsyncKeyState(a_iKeyCode) And &H8000) Then ChkKeyPush = True
'// 指定キーが押されていない Else ChkKeyPush = False End If End Function
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
81
82
83
84
Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Declare PtrSafe Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As
Integer
Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
 
Sub GetAsyncKeyStateTest()
    Dim bEndFlg     As Boolean      '// ループ終了フラグ(True:ループ継続、False:ループ終了)
    Dim lStartTimer As Long         '// 基点時刻
    Dim sMsg        As String       '// 出力MSG
    
    '// ループ継続
    bEndFlg = True
    
    '// 基点時刻を取得
    lStartTimer = GetTickCount
    
    Do
        '// ループ終了フラグが「ループ終了」の場合
        If bEndFlg = False Then
            '// ループを抜ける
            Exit Do
        End If
        
        Dim bKeyA       As Boolean
        Dim bKeyShift   As Boolean
        
        sMsg = ""
        
        '// Qが押された場合
        If (ChkKeyPush(vbKeyQ) = True) Then
            '// ループ終了フラグを「ループ終了」に更新
            bEndFlg = False
            Debug.Print "Quit"
        End If
        
        '// Aが押された場合
        If (ChkKeyPush(vbKeyA) = True) Then
            bKeyA = True
        Else
            bKeyA = False
        End If
        
        '// Shiftが押された場合
        If (ChkKeyPush(vbKeyShift) = True) Then
            bKeyShift = True
        Else
            bKeyShift = False
        End If
        
        '// Aが押されていた場合
        If bKeyA = True Then
            sMsg = "A "
        End If
        '// Shiftが押されていた場合
        If bKeyShift = True Then
            sMsg = sMsg & "Shift "
        End If
        
        If sMsg <> "" Then
            Debug.Print sMsg
        End If
        
        '// Windowsに制御を渡す
        DoEvents
        
        '// 0.1秒経過するまでスリープ
        Do While GetTickCount - lStartTimer < 100
            '// CPUを休ませる(ループ処理にCPUが占有されないようにして負荷を下げる)
            Call Sleep(1)
        Loop
        
        '// 基点時刻を再取得
        lStartTimer = GetTickCount
    Loop
End Sub
 
Function ChkKeyPush(a_iKeyCode)
    '// 指定キーが押された
    If (GetAsyncKeyState(a_iKeyCode) And &H8000) Then
        ChkKeyPush = True
    '// 指定キーが押されていない
    Else
        ChkKeyPush = False
    End If
End Function

実行結果
AキーとShiftキーをそれぞれ押して、Qキーで終了した場合の例です。キーを押し続けていると連続して出力されます。

A
A
Shift
Shift
Shift
A Shift
A Shift
A Shift
A Shift
A Shift
A Shift
Shift
Quit


関連記事


Tweet

0


投稿ナビゲーション

VBAの処理を一時停止する(Sleep、Wait)






カテゴリー

 * .NET Framework (2)
 * FileSystemObject (35)
 * VBA (109)
 * Win32API (4)
 * WScript (11)
 * イベント (3)
 * ウィンドウ (23)
 * エラー (16)
 * シート (40)
 * セル (153)
 * ファイル (18)
 * ブック (29)
 * 印刷 (1)
 * 困ったとき (29)
 * 基本 (7)
 * 文字列 (21)
 * 配列 (15)
 * 配列ソート (6)
 * 関数 (57)
 * 高速化 (6)


TWITTER





最近の投稿

 * VBAでファイルをゴミ箱に移動させる(SHFileOperation)
 * VBA実行時に確認メッセージを非表示にする
 * リボンやクイックアクセスツールバーを他PCへ引き継ぐ方法
 * マクロをいつでもすぐ使えるようにする(クイックアクセスツールバー)
 * ブックを開いた時に実行するマクロの活用方法(Workbook_Open)

 1. Excel作業をVBAで効率化 TOP
 2. Win32API
 3. どのキーが押されたのかをVBAで判定する(GetAsyncKeyState)

© 2022 Excel作業をVBAで効率化
 * シェア
 * お問合わせ
 * TOPへ

Tweet

0
+1