TL;DR —
Workbook_BeforeCloseは、誰かがブックを閉じようとした瞬間に — どのウィンドウも破棄される前に — Excel が発火させるイベントです。渡される 引数はただ 1 つ、Cancelで、この引数こそがすべての要です:Cancel = Trueにすれば閉じる操作は取り消され、ファイルは開いたまま残ります。つまりこれは、出て いくついでに走らせる見送りの置き手紙ではなく、戸口でユーザーを引き返させられる チェックポイントなのです。コードはThisWorkbookに置かねばならず、Excel にも 保存を尋ねさせずに自前の保存プロンプトを走らせたいなら、ThisWorkbook.Saved = Trueで組み込みダイアログを黙らせます。
' ThisWorkbook に置く — Module ではない。
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If ThisWorkbook.Sheets("Form").Range("Approved").Value <> "Yes" Then
MsgBox "Set Approved = Yes before closing this file.", vbExclamation
Cancel = True ' <-- 閉じる操作を取り消す。ブックは開いたまま
End If
End Sub
ほとんどのマクロは、ユーザーが求めたときに走ります — ボタン、ショートカット。
Workbook_BeforeClose は、ユーザーが立ち去ろうとしたときに走り、そのタイミングが、
これを同じだけ便利で危険なものにしています。「保存されていない変更があります」、
強制サインオフ、終了時の後始末、「誰がファイルを閉じたかを記録する」— その裏に
いるイベントです。同時に、いちばん配線を間違えられやすいイベントでもあります —
Excel が実際には拒否権を差し出しているのに、それを単なる通知として扱ってしまうから
です。
この記事で学べること
- 考え方の軸 — 単なる予告ではなく、
Cancelという拒否権を手渡す門番イベント - 実際に閉じる操作を止める方法(そして
vbYesNoのプロンプトだけではなぜ止まらないか) - 二重プロンプトの罠 — そしてそれを直す
ThisWorkbook.Saved = Trueというレバー BeforeCloseを決してデータ保全の安全網にしてはならない理由(クラッシュは飛ばす)- シートを閉じる対アプリ終了 — そしてコードを置かねばならない場所
考え方の軸:見送りの置き手紙ではなく、門番
Workbook_BeforeClose は、閉じる要求が出た瞬間に発火します — ウィンドウの ×、
ファイル ▸ 閉じる、Application.Quit、あるいは別のマクロが .Close を呼んだとき —
そして Excel が取り返しのつかないことをする前に発火します。決定的なのは、Cancel
という名の Boolean を 1 つ、参照渡しで手渡す点です。この Sub をあなたが呼ぶことは
ありません。Excel があなたを呼び、あなたが終わるのを待ち、それから Cancel が何に
設定されたかを見るのです。
その 1 つの引数が、すべてを捉え直させます。Cancel に触れずに放っておけば、閉じる
操作は通常どおり進みます。Cancel = True にすれば、Excel は閉じる操作を放棄し、
ブックはそのまま開いたまま残ります。つまりハンドラーは門番です:ユーザーを
通してやることも、押しとどめて何かを直しに帰らせることもできます。「閉じる前に
知らせて」と読んでしまうと、このイベントの力をまるごと取り逃がします。
シグネチャは固定で、置き場所は交渉の余地がありません:
Private Sub Workbook_BeforeClose(Cancel As Boolean)、これを ThisWorkbook の
コードモジュールに置きます — プロジェクトエクスプローラーの「Microsoft Excel
Objects」の下で ThisWorkbook をダブルクリックします。標準の Module に入れれば
決して発火しません。これは Workbook_Open と同じ
「コードはどこに置くか」のルールです。
いちばん大事なルール:Cancel なしのプロンプトは芝居にすぎない
これは、ほぼ誰もが最初にやる間違いです。閉じる前に確認したいので、こう書きます:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
MsgBox "Are you sure you want to close?", vbYesNo ' 尋ねる…そして答えを無視する
End Sub
ダイアログが出て、ユーザーが No をクリックする — それでもブックは閉じてしまい
ます。このコードのどこも Cancel に触れていないので、Excel はそのまま進みます。
プロンプトはただの芝居でした。ルールはこうです:強制したい判断は、Cancel に
書き戻さねばならない。 答えを受け取り、それに従って動きます:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If MsgBox("Close without exporting the report?", vbYesNo + vbQuestion) = vbNo Then
Cancel = True ' ユーザーが No -> 閉じる操作を中止
End If
End Sub
これで「No」が実際にファイルを開いたまま保ちます。これが、知らせるイベントと
守るイベントの違いです:Cancel = True に到達できて初めて、門番は本物になります。
ユーザーを苛立たせる罠:Excel が二度保存を尋ねる
第二の古典的なバグは、ハンドラーが保存した瞬間に現れます。「出ていく前に保存する」 ステップを足すと、変更のあるブックを閉じるときに2 つのダイアログが出ます: あなたのプロンプト、それから Excel 自身の「変更を保存しますか?」。理由は、あなたと Excel の両方が、同じ未保存状態を処理しようとしているからです。
レバーはブックの Saved プロパティです。Saved は「未保存の変更はない」を表す
Excel のフラグです。これを True にすると、あなたは Excel に「ファイルはクリーンだ」
と告げていることになり — たとえ本当に編集があっても、Excel は自分の保存プロンプトを
出さずに閉じます。保存は自分で処理して、それから Saved を切り替えます:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim answer As VbMsgBoxResult
answer = MsgBox("Save changes before closing?", vbYesNoCancel + vbQuestion)
If answer = vbCancel Then
Cancel = True ' まったく閉じない
ElseIf answer = vbYes Then
ThisWorkbook.Save ' 保存してから、閉じる処理を続行させる
Else ' vbNo
ThisWorkbook.Saved = True ' 破棄:クリーンだと Excel に伝え、再度尋ねさせない
End If
End Sub
Saved = True にしても、何かが保存されるわけではありません — ブックはすでに保存
済みだと主張することで、プロンプトを抑制するのです。意図をもって使いましょう:
これは、あなた自身の保存ロジックを、ユーザーが目にする唯一の保存ロジックにする
スイッチです。
データを守るルール:BeforeClose は安全網ではない
Workbook_BeforeClose は、あらゆる秩序ある閉じ方で発火します — ×、ファイル ▸
閉じる、.Close、Application.Quit。Excel がクラッシュしたとき、プロセスが強制終了
されたとき、あるいは停電のときには発火しません。これがひとつの誘惑を危険にします:
BeforeClose を、データを保存する唯一の場所には決してしないこと。「唯一のコピーを
保存する」ロジックがここに置かれ、Excel が死ねば、そのコピーは消えます。BeforeClose
は儀礼(促す、片づける、記録する)として扱い、本当の永続化は、明示的な保存か、時間
起動の自動保存に持たせましょう — 出口の経路ではなく。
もうひとつ知っておくべき境目:ユーザーが複数のブックを開いたまま Excel 自体を終了
しているとき、各ブックがそれぞれ自分の BeforeClose を受け取ります。そのうちの
ひとつで Cancel = True にすれば、そのブックは閉じるのをやめますが、すでにハンドラー
を走らせ終えた兄弟たちは、中途半端に閉じかけた状態になっているかもしれません。終了
時に「全か無か」が必要なら、Workbook_BeforeClose からの調整は控えめにして、複数
ファイルのケースをテストしましょう。
人がつまずく区別:BeforeClose と BeforeSave
BeforeClose は出口を守ります。その兄弟 Workbook_BeforeSave
は保存を守り、同じ種類の Cancel 拒否権(プラス SaveAsUI フラグ)を手渡します。
両者は自然に組みます — 保存時に検証し、閉じるときに確認する — そして両方とも、この
イベント一族の黄金律を共有します:ハンドラーがセルに書き込んだり保存したりするなら、
その動作を Application.EnableEvents = False … = True で囲み、エラーハンドラーの中で
元に戻すこと。ちょうど Worksheet_Change がそうする
ように、自分の書き込みが仕掛けを再発火させられないようにするためです。
ExcelMaster の活用
閉じる門番は、微妙に間違えやすい 4 つの小さな判断です:ThisWorkbook に置く(Module
ではない)コード、実際に閉じる操作を止める Cancel の書き込み、二重プロンプトを
避ける Saved = True の切り替え、そして出口の経路に何を任せないかという判断です。
ExcelMaster
なら、代わりに振る舞いを述べられます。「このファイルが閉じる前に、Approved セルが
Yes でなければ、閉じるのを止めてユーザーに伝えて」と言えば、ThisWorkbook に
Workbook_BeforeClose を書き、Cancel を正しく設定し、保存プロンプトを二重にせずに
処理し、どんな書き込みもガードします。ブックもコードもあなたのもの。どのレバーが何を
するかを学ぶ試行錯誤だけを飛ばせるのです。
よくある質問
VBA でブックが閉じるのを止めるには?
Workbook_BeforeClose の中で Cancel 引数を True に設定します:
Private Sub Workbook_BeforeClose(Cancel As Boolean) … Cancel = True。Excel は
Cancel を参照渡しで渡し、ハンドラーが走ったあとにそれを調べます。True なら閉じる
操作を中止し、ファイルは開いたまま残ります。MsgBox 単体では何もしません — 判断を
Cancel に書き戻す必要があります。
閉じるときに Excel が二度保存を尋ねるのはなぜですか?
あなたの BeforeClose ハンドラーが保存を促し、かつ Excel はまだ未保存の変更が
あると考えているので、Excel も自分のダイアログを出すからです。保存は自分で処理して、
それから ThisWorkbook.Saved = True を設定し、ブックはクリーンだと Excel に伝え
ましょう — それが組み込みの「保存しますか?」プロンプトを抑制します。
Workbook_BeforeClose のコードはどこに書きますか?
ThisWorkbook のコードモジュールの中です — プロジェクトエクスプローラーの
「Microsoft Excel Objects」の下で ThisWorkbook をダブルクリックし、そこに
Private Sub Workbook_BeforeClose(Cancel As Boolean) を貼り付けます。標準の Module
からは発火せず、マクロが有効である必要があります。
Excel がクラッシュしたら Workbook_BeforeClose は走りますか?
いいえ。発火するのは秩序ある閉じ方のときだけです — ウィンドウの ×、ファイル ▸
閉じる、.Close、Application.Quit。クラッシュ、強制終了されたプロセス、停電は、
これを丸ごと飛ばします。BeforeClose を、データを保存する唯一の場所に頼っては
いけません。
BeforeClose は Workbook_BeforeSave とどう違いますか?
BeforeClose はファイルが閉じるときに発火し、閉じる操作をキャンセルできます。
Workbook_BeforeSave はファイルが保存されるときに発火し、保存をキャンセルできます。
変更のあるファイルを閉じると通常は保存プロンプトが出るので、両者はしばしば一緒に
現れますが、これらは別々の Cancel フラグを持つ別々のイベントです。
検証環境
検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-05。
関連ガイド: VBA Workbook_BeforeSave · VBA Worksheet_Activate & Deactivate · VBA Workbook_Open · VBA Worksheet_Change · VBA On Error
