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のアセンブリだと分かるかもしれない。
まとめ
脳筋は正義。