はじめに
Excel VBAでApplication.OnTimeを使って1秒ごとに処理を実行するマクロを作ったところ、Excel上で何も操作しないと、処理が数秒(たとえば5秒)遅延する現象が発生しました。
この記事では、この問題の原因と、実際に有効だった解決策を紹介します。
※この記事は調べたことをGitHub Copilotにまとめてもらったものです。
問題の発生
- Excel VBAの
Application.OnTimeで1秒ごとに処理を実行 - しばらくExcelに触らず放置していると、OnTimeによる処理が遅れる(5秒ごとに動くような感じ)
- マウスでExcelを操作したり、ウィンドウをアクティブにしたりすると、また1秒ごとに動き出す
原因
この現象は、Application.OnTimeの仕様やExcelの内部挙動に起因しています。
- ExcelはVBAやユーザーインターフェース(UI)、計算などをシングルスレッドで処理しています
- Excelがアイドル状態(非アクティブ・ユーザー操作がない)になると、VBAのタイマーイベント(OnTime)が遅延することがある
- Excelの内部的なイベントループが「ユーザー操作」をトリガーに動きやすくなるため、放置しているとOnTimeが遅延しやすい
解決策
擬似的なマウス移動でExcelに「操作あり」と認識させる
mouse_event APIを使い、擬似的にマウスをほんの少し動かすことで、Excelに「ユーザーが操作している」と認識させ、OnTimeの遅延を防ぐことができます。
サンプルコード
#If VBA7 Then
Private Declare PtrSafe Sub mouse_event Lib "user32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As LongPtr)
#Else
Private Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
#End If
Const MOUSEEVENTF_MOVE = &H1
' マウスをほんの少し右→元に戻す
Sub MoveMouseSlightly()
mouse_event MOUSEEVENTF_MOVE, 1, 0, 0, 0
mouse_event MOUSEEVENTF_MOVE, -1, 0, 0, 0
End Sub
使い方例
OnTimeで定期実行する処理の中で、MoveMouseSlightlyを呼ぶだけです。
Sub TimerProc()
' ここに本来の処理を書く
MoveMouseSlightly
' 次回のOnTime予約
Application.OnTime Now + TimeValue("00:00:01"), "TimerProc"
End Sub
注意点
- サーバーやリモートデスクトップ環境ではこの方法が効かないことがあります
- セキュリティソフトや環境によってはAPIコールがブロックされることがあります
- あくまで「Excelの現仕様の裏技的回避策」です
- 厳密なリアルタイム制御が必須な場合、VBAではなく外部タイマーや他言語の利用を検討してください
参考リンク
- Microsoft Q&A: OnTimeの遅延現象
- Stack Overflow: Application.OnTime delays when there is no user interaction even if Excel is active
まとめ
Excel VBAのApplication.OnTimeは、ユーザー操作がないと遅延することがあります。
擬似的なマウス移動を発生させることで、この遅延を回避できる場合があります。
同じ現象で悩んでいる方の参考になれば幸いです。