銀河鉄道

VBA|保存処理(SaveAs)がフリーズする原因と対策

サムネイル
[VBA]Killthe progress dialog

進捗ダイアログを出さないことで、保存後の“見えないフリーズ”は消える。

Kill the progress dialog, kill the freeze.

SaveAsの前後にアラートを止めるコード

#If VBA7 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As Long)
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
#End If

Public Sub SaveWorkbookScoped(ByVal wb As Workbook, ByVal path As String, ByVal fmt As XlFileFormat)
    Dim prevAlerts As Boolean, prevEvents As Boolean
    On Error GoTo CLEANUP

    ' --- save current states
    prevAlerts = Application.DisplayAlerts
    prevEvents = Application.EnableEvents

    ' --- suppress UI
    Application.EnableEvents = False
    Application.DisplayAlerts = False

    ' --- risky I/O
    wb.SaveAs FileName:=path, FileFormat:=fmt
    wb.Close SaveChanges:=False

    ' --- let UI breathe
    DoEvents
    Sleep 100

    ' success path → always cleanup
    GoTo CLEANUP

CLEANUP:
    ' restore no matter what
    Application.DisplayAlerts = prevAlerts
    Application.EnableEvents = prevEvents
End Sub

Why this freezes|なぜ固まるのか

  • Excelは長いI/O時に進捗ダイアログを出す。
    • それにより、VBA実行が待機状態になることがある。
  • Application.DisplayAlerts=False は「ユーザー入力のダイアログ」を既定の応答で処理するため、早く終了できる。
    • ただし公式は、進捗自体を明示的に“消す”とは書いていない点に注意(Microsoft Learn)

Notes|補足

  • DoEvents はOSへ制御を渡し、メッセージキュー処理をさせるので固まり解消に寄与。
  • Sleep はWindows API。64bitでは PtrSafe 必須。

Resource Release Lag|リソース解放の遅延とは

Excelが「保存完了」のあと、ファイルやUIリソースをすぐに解放できずに一瞬つかんだままになる状態。

  • UI Thread Freeze|UIスレッドのフリーズ は、
     その“解放待ち”の処理をUIスレッド(画面を担当するスレッド)が抱えてるせいで、
     画面もVBAも動かなくなる(応答なしに見える)現象のこと。

Excelの中には「2つの世界」がある

種類役割
UI Thread(ユーザーインターフェイススレッド)画面描画、ダイアログ表示、メニュー操作など「保存中…」ダイアログ、進捗バー、マウスカーソルの砂時計表示
Worker Thread(バックグラウンド処理)ファイルI/Oや計算、保存の実行SaveAs の実処理、ディスク書き込み、再計算

普通は、
→ Worker Thread がファイル保存を終えたら
→ UI Thread に「終わったで」って通知して
→ 進捗ダイアログを閉じる、画面を再描画する、って流れになる。

Resource Release Lag|リソース解放の遅延

でも大量ファイル保存とかネットワーク越しやと、
Worker Threadが完了通知を送っても、Excel内部のCOMオブジェクトやファイルハンドルがまだ解放されてない。
UI側は「終わった通知を受け取ったのに、裏でまだ握られてる」と混乱する。

つまり:

“終わったっぽいけど、実際はまだ掴んでる” — これがリソース解放ラグ。

この瞬間、UIスレッドは「待機状態」に入って、
画面が固まったように見える(Stuck状態)

UI Thread Freeze|UIスレッドのフリーズ

ExcelはシングルUIスレッド構造やから、
進捗ダイアログを出してる時にそのスレッドが止まると、
画面の更新もVBAの実行も止まる

たとえば:

NewBook.SaveAs ...
' ←ここでUIがフリーズ

ってなると、
後続の DisplayAlerts = FalseClose も実行に進めない。

Progress Dialog|進捗ダイアログの負担

「保存中… [キャンセル]」っていう小さいダイアログも、実はUIスレッドの管轄。
ファイルI/O中ずっとそのUIを描画・更新してるから、
ディスクやネットワーク遅延があるとCPUスレッドが飽和して、
UIが「描画更新待ち」に陥る=Stuckする

しかもその進捗ウィンドウはVBAとは別の“モーダルUI”扱いになるから、
裏でコードが「完了を待機」する設計になってる。
→ 保存自体は完了してるのに、「UIが閉じない」せいでコードが動かない、というズレが発生。

だからこうなる

ファイルは保存されてる
でもUIスレッドが「終わった通知」を受け取れず固まってる
→ 結果として「キャンセルを押すと動く」ように見える。

対処の本質

  • DisplayAlerts = False でUI進捗を出さない
  • DoEvents でOSに制御を戻す(UIスレッドに処理させる)
  • Sleep で微小時間の同期を取る
    これが、リソース解放ラグ+UIスレッドフリーズの両方に効く構造的対策。

保存完了=ファイルが書けた、ではなく、
「ExcelがUIごと全部片付けた」まで待たなければならない。

| UI Thread|ユーザーインターフェイスの主スレッド |
| Worker Thread|実処理担当スレッド |
| Resource Release Lag|リソース解放の遅延 |
| Progress Dialog|進捗ダイアログ |
| Stuck|描画・操作が止まる状態 |
| DoEvents|処理をOSに一時委譲 |
| Sleep|短いウェイトで同期を取る |
| Modal UI|操作をブロックするUI |
| COM Handle|内部リソースハンドル |

DoEvents + Sleep 100 はなぜ必要?

DoEvents で「Windowsに処理を渡す」
Sleep 100 で「CPUを0.1秒休ませる」

Excelが内部でリソース解放やUI更新を完了する時間を与えるために入れる。
たとえば、

  • 保存直後 (SaveAsClose) はファイルI/Oが終わっても、
    Excel内部のCOMリファレンス解放や画面再描画がまだ残ってる。
  • その状態で次の処理(別ブック開く、フォルダ操作など)に進むと、
    一瞬“競合”や“再ロック”が起きて固まるケースがある。

これでExcelが追いつく猶予を与える。

いつ要るか・いつ要らないか

状況推奨
ローカル保存で軽いファイルほぼ不要。速度優先で削ってOK
ネットワークドライブ/OneDrive保存あった方が安定(解放遅延が起こりやすい)
繰り返し大量保存するバッチ処理挟まないとリソースリークやUIフリーズリスクあり
Excelを閉じる直前だけ1回だけなら「保険」としてアリ

まとめ

  • DoEvents → UI・イベントの残処理を完了させる。
  • Sleep → OSレベルで短時間ウェイトを入れる。
  • 両方セットで「同期の保険」。
  • だが、常時必須ではない。ネットワーク保存や連続I/O処理の時だけで充分。

短く言うと:

普通の保存なら要らない。ネットワーク越しや連続保存のときだけ、保険で入れる。

| DoEvents|処理をOSに明け渡す |
| Sleep|短時間停止(CPUクールダウン) |
| Cleanup Sync|後処理の同期 |
| I/O Release Lag|入出力の遅延解放 |
| Insurance Wait|保険的待機 |

Referenced Insights & Citations

| Problem Isolation|問題の切り分け |
| UI Thread Freeze|UIスレッドのフリーズ |
| Progress Dialog|進捗ダイアログ |
| DisplayAlerts|表示警告の抑止 |
| DoEvents|イベント処理の明け渡し |
| Sleep (PtrSafe)|スリープ(PtrSafe宣言) |
| Silent Execution|サイレント実行 |
| Cleanup/FInally Pattern|後始末(Finally相当) |
| SaveCopyAs|コピー保存 |
| Best Practices|ベストプラクティス |

著者

author
月うさぎ

編集後記:
この記事の内容がベストではないかもしれません。

記事一覧