この記事について
この記事はMicrosoft LearnのVBA DoEvents公式資料を確認し、Excel VBAのイベント処理と再入の観点まで掘り下げて整理しています。検証ステータス:📘 公式仕様確認済み・Excel実機未確認
DoEventsは長時間処理を本当の非同期処理へ変える機能ではありません。
Excel VBAで長いループを実行すると画面が固まったように見えることがあります。DoEventsを呼ぶと操作を受け付けやすくなるのは、VBAが一時的に制御をOSへ渡し、待っているイベントを処理する機会を作るためです。
ここで重要なのは、DoEventsを「非同期化する命令」と考えないことです。
DoEventsなしでは何が起きるか
sequenceDiagram
participant U as User
participant E as Excel UI
participant V as VBA
U->>E: Click / repaint等のイベント
V->>V: 長いloopを継続
Note over E,V: VBAが処理を占有
E-->>U: 反応しにくく見える
VBAが長い処理を続ける間、UI側がイベントを処理する機会が減ると、再描画や入力への反応が遅れます。
DoEventsを呼ぶと制御が戻ってくる
Microsoft Learnでは、DoEventsはOSが他のeventを処理できるようexecutionをyieldする関数として説明されています。
sequenceDiagram
participant V as VBA
participant O as Excel / OS event processing
participant U as User
V->>V: loopを実行
V->>O: DoEvents
U->>O: input / repaint
O->>O: queue中のeventを処理
O-->>V: controlを戻す
V->>V: loopの続きを実行
DoEventsの前後で処理本体が別threadへ移動するわけではありません。
最小の例
Sub DemoDoEvents()
Const MaxCount As Long = 5000000
Dim i As Long
On Error GoTo CleanUp
For i = 1 To MaxCount
If i Mod 10000 = 0 Then
Application.StatusBar = _
"Processing: " & Format(i / MaxCount, "0%")
DoEvents
End If
Next i
CleanUp:
Application.StatusBar = False
End Sub
毎loopでDoEventsを呼ばず、一定間隔でUIへ制御を返しています。
便利だが、再入という問題がある
DoEvents中は別のevent procedureが実行される可能性があります。
たとえばButton_Clickから長い処理を開始し、その途中のDoEventsで同じButtonが再び押されると、同じ処理が重なって開始される可能性があります。
flowchart TB
A[Button_Click] --> B[長い処理]
B --> C[DoEvents]
C --> D{同じbuttonが再度押された?}
D -->|No| E[元の処理へ戻る]
D -->|Yes| F[同じevent procedureへ再入]
F --> G[状態の競合・二重実行]
Microsoft Learnも、DoEventsでcontrolを渡している間に同じprocedureが再実行されないよう注意を促しています。
単純な再入防止
既存マクロの小規模な対策なら、実行中flagを使えます。
Private mRunning As Boolean
Sub LongTask()
Const MaxCount As Long = 5000000
Dim i As Long
If mRunning Then
MsgBox "すでに実行中です。"
Exit Sub
End If
mRunning = True
On Error GoTo CleanUp
For i = 1 To MaxCount
If i Mod 10000 = 0 Then
Application.StatusBar = _
"Processing: " & Format(i / MaxCount, "0%")
DoEvents
End If
Next i
CleanUp:
Application.StatusBar = False
mRunning = False
End Sub
これはthread-safeな並行処理制御ではありません。Excel VBAで同じ操作を二重開始しにくくするための単純な状態管理です。
DoEventsと非同期処理を分ける
flowchart LR
A[DoEvents] --> B[イベント処理へ一時的にcontrolを渡す]
C[非同期I/O] --> D[待ち時間をcallback/event等で扱う]
E[別process/thread] --> F[処理主体そのものを分ける]
B -. 同じではない .- D
B -. 同じではない .- F
HTTP通信など、外部I/Oの待ち時間が長い処理なら、対象APIが提供するasync modeやevent/callbackを使う設計の方が本質的な場合があります。
DoEventsは「同期処理をasyncへ変換する魔法」ではありません。
Windowsのmessage loopとの関係はどこまで同じか
VBA/Excelの内部実装を単純にWin32の典型的なGetMessage/DispatchMessage loopと同一視するのは避けます。
一方で、Windows GUIがmessage/eventを処理しながら動くという考え方を学ぶ入口として、DoEventsは面白い題材です。
flowchart TB
A[Windows GUIのevent/message] --> B[Excel application]
B --> C[VBA event procedure]
C --> D[長い同期処理]
D --> E[DoEvents]
E --> B
「VBAのDoEventsの内部コードがこの図そのもの」と断定するのではなく、イベント駆動を理解する概念図として扱います。
いつ使うか
DoEventsが役立ちやすいのは、小さな既存マクロで次を行いたい場合です。
StatusBarなどの進捗を更新したい
キャンセル操作を受け付けたい
UIを完全に無反応に見せたくない
一方で、長時間・複雑な処理、状態共有が多い処理、外部I/O待ちが中心の処理では、別の設計を検討します。
まとめ
DoEventsはOSへ一時的にcontrolを渡してeventを処理させる
非同期処理そのものではない
UIの反応を改善できる場面はある
DoEvents中の再入に注意する
長いI/O待ちはasync APIや別設計を検討する
Windowsのevent/message処理を学ぶ入口として使えるが、内部実装を単純化しすぎない

コメント