情シス担当の備忘録

VBA・労働法とか。

【ExcelVBA】keybd_eventをつかってCitrix経由で起動しているアプリにキー入力を送る。(手作りRPA)

仕事の中で、大量の単純作業を処理する必要がありました。
Citrix経由で起動しているアプリにデータを打ち込んでいく作業です。
1日あたり500件あることもあり、それが週3~4で続いていく・・・。
チームで作業しているので1人あたりの作業量は100件程度になりますが、抜本的なシステム改修がある予定もなく、毎日ウンザリでした。

しかし、データ入力作業を分析すると、キー入力だけですべて再現できることがわかりました。そのため、まずはVBAの「SendKey」で行おうと思いましたが、なぜかうまくいきません(厳密に言えば、数字やアルファベットの入力はできますが、TABやALTなどがうまくいきません)。その後Win32APIのSendInputも試しましたが、ダメでした。ネットで調べてみると、同様に「Citrix経由で起動しているアプリにキー入力をする」ことで悩んでいる人が多く居て、Wind32APIの「keybd_event」を使うと上手くいくらしいことがわかりました。

「keybd_event」を実際に使ってみるとうまくいったので、作業を無事自動化できました。以下、備忘録も含めて、やってみたことの解説です。


※Wind32APIを使うので、環境としてはWindowsのみです。

【参考にしたページ】
www.mrexcel.com
↑自分と同じことで悩んでいた方

https://www.codeproject.com/Articles/7305/Keyboard-Events-Simulation-using-keybd-event-funct
↑スキャンコード

https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/keycode-constants
↑キーコード定数

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-keybd_event
↑keybd_event関数についてのMSのヘルプ


【keybd_eventの使い方】
keybd_eventは、キー入力が行われたときにキーボードからOSに送信される信号(スキャンコードといいます)を再現することで、キー操作を再現します。
スキャンコードには、「メイクコード」と「ブレイクコード」があります。「メイクコード」は「キーが押された」という信号であり、「ブレイクコード」が「キーが離された」という信号です。

上述のとおり、keybd_eventはVBA本体の機能ではなく、WIN32APIの機能なので、この機能を呼び出すための一文をモジュールの宣言セクションに書きます。

Private Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)


その後、subプロシージャの中にkeybd_eventを書いていきます。
例としてはこんな感じです。

sub 例()
keybd_event &H9, &HF, 0, 0 'tab押す(メイクコード)
keybd_event &H9, &H8F, &H2, 0 'tab離す(ブレイクコード)
end sub

この例では、TABキーのメイクコードを送ったあとブレイクコードを送ることで、TABキーが1度押されたことを再現しています。

keybd_eventには4つの引数があります。
①bVk
仮想キーコードを入力します。
上記のTABの例だと、「&H9」が該当します。仮想キーコードは、メイクコードを送る時もブレイクコードを送る時も同じです。
各キーの仮想キーコードについては、ググってください。

②bScan
スキャンコードを入力します。
上記のTABの例だと、メイクコードの際の「&HF」、ブレイクコードの際の「&H8F」が該当します。ネットで調べると、この引数は使わない旨解説しているサイトもいくつかありましたが、自分の環境では使用しないと動きませんでした。
各キーのメイクコード・ブレイクコードについては、ググってください。

③dwFlags
ブレイクコードを送るときに、セットで「&H2」を入力します。
理屈を完全に理解できているわけではありませんが、「&H2」をセットしないと動きませんでした。

④dwExtraInfo
使いません(常に0)

後は、インターネットで仮想キーコードとメイクコード・ブレイクコードを調べばキー操作を再現できます。ただ、3点注意が必要です。
①メイクコードはブレイクコードは常にセットで送信する方が無難と考えます。
例えば、altキーのメイクコードを送信した後にブレイクコードを送信しないと、OSレベルでaltキーが押しっぱなしになってしまって、再起動しないと直りません。

②キー操作はアクティブなウィンドウに送信されるため、キー操作を送信する前にWSHshellのappactivateを使ってアクティブにしたいウィンドウを指定します。

③私の環境では、なぜか左ctrlキーのメイクコードとブレイクコードを送った後、右ctrlキーのブレイクコードも送らないと、ctrlキーが押されっぱなしになってしまいました。


以上を踏まえたサンプルコードは、以下のとおりです。

Private Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

sub()
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")

wsh.AppActivate ("ウィンドウ名1")
   Application.Wait [Now() + TimeValue("00:00:00.5")] '画面遷移待機

   keybd_event &H9, &HF, 0, 0 'tab押す
   keybd_event &H9, &H8F, &H2, 0 'tab離す

   keybd_event &HA2, &H1D, 0, 0 '左ctrl押す
   keybd_event &H56, &H2F, 0, 0 'V押す
   keybd_event &H56, &HAF, &H2, 0 'V離す
   keybd_event &HA2, &H9D, &H2, 0 '左ctrl離す
   keybd_event &HA3, &H1D, &H2, 0 '右ctrl離す 
   'なぜか右ctrlも開放しないといけない

   keybd_event &H12, &H38, 0, 0 '左alt押す
   keybd_event &H53, &H1F, 0, 0 'Sを押す
   keybd_event &H53, &H9F, &H2, 0 'Sを離す
   keybd_event &H12, &HB8, &H2, 0 '左alt離す

Set wsh = Nothing
wsh.AppActivate ("マクロ有効エクセル.xlsm")

 
MsgBox "処理が完了しました"
   
end sub

上記の例では、shellオブジェクトを作成後AppActivateで対象のウィンドウをアクティブにした後キー入力を送り、処理が終わったらマクロ有効エクセルを再度アクティブにして、msgboxを表示しています。


同じことで悩んでいる方の参考になれば幸いです。