1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

VBAのSendKeysの同期化

Last updated at Posted at 2021-08-20

SendKeys

セルに「a」を入力しようとしたが、
Application.SendKeys "a{ENTER}", True
CreateObject("WScript.Shell").SendKeys "a{ENTER}", True
のどちらも、Wait:=Trueでは同期できなかった。

SendKeysはキー入力を送るだけであり、
キーバッファからシステムが受け取れたかは確認されない。
Waitでも、ある程度は待つが同期は約束されない。

このため、セルへの入力が記述箇所で行われず、
マクロの最後にまとめて入力された。

SendKeysの直後で処理を止めてみる。

Waitメソッド

Application.Wait (Now + TimeValue("0:00:01"))
結果:実感できず。
入力を受け取るセルも止まるのでは?

WindowsAPIのSleep

(64bitの場合)
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
(32bitの場合)
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
を先頭に書く。
Sleep (1000)
結果:少し同期される(体感2割)

DoEvents関数

DoEvents
結果:まあまあ同期される(体感1割)

正攻法

DoEventsは、貯まっている非同期処理の完了の催促(らしい)
ここでは、キー入力と、それを受ける再描画に必要となる。
DoEvents単体では、0.数秒しか待ってくれないから、
入力の催促した後に、SleepかDoEventsで待つ必要がある。
再描画に対しても、DoEventsで再度催促すると安定する。

DoEvents        '入力催促
Sleep (1000)    '実行
DoEvents        '描画催促

(体感6割)時間が無駄にかかる。

脳筋

DoEvents を連続で20行くらい書く(体感9割)
同じだけSleepで待つより、反応が良い。

貯まっている処理があれば催促され、0.数秒待ってくれる。
貯まってなければ時間はそんなにかからない。
よって、早い。

ループの使用

Do系ループで回すと、待機するタイミングが記述した箇所とは限らないため、
For系ループで回すか、物理的にたくさん書くこと。

使える

  • For - Next
  • For Each - Next

使えない

  • Do While - Loop
  • Do Until - Loop
  • Do - Loop While
  • Do - Loop Until
  • While - Wend

モジュール

Do系For系を問わず、DoEventsをループさせるだけのSubを個別に作り、Callさせると上手くいく。

For系は内部で、別Subのようにスタックが積まれていくのかもしれない。
VBのアセンブリだと分かるかもしれない。

まとめ

脳筋は正義。

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?