Application.Quitの誤解

マクロの処理が終わる段階でExcelも終了させたいということで、「Quitメソッド」を発行させるわけですが、この瞬間に終了するとの誤解があるようです。
仕様が変わったのでしょうか?   Excel2003までは以下のように説明していたのです。実際にこのように動作していたと思います。
Closeメソッドを発行しないままでQuitメソッドを発行する。

Option Explicit

'-------------------------------------------------------------------------------
' ■CloseはせずにQuitを発行します。
Sub Quit_TEST1()
    MsgBox "CloseはせずにQuitを発行します。"
    Call Quit_TEST1_Sub
    MsgBox "終わらないよ!!(TEST1)"
End Sub

' Closeはしないで終了するサブ処理
Private Sub Quit_TEST1_Sub()
    ' 保存確認を避けるため、保存済みにする
    ThisWorkbook.Saved = True
    ' 他にブックが開いていなければ、Excelを終了する
    If Workbooks.Count <= 1 Then Application.Quit
End Sub
Quit_TEST1」プロシージャを起動させると、「Quit_TEST1_Sub」が呼び出され、その中でQuitメソッドが発行されます。そこでExcelが終了してしまえばそれまでですが、終了しなければ呼び元プロシージャに戻って、「終わらないよ!!」メッセージが表示されます。
しかし、2017年11月現在、Excel2010、2013、2016で見る限りこれは正しくありません。 「終わらないよ!!」は表示されますが、その後Closeメソッドを発行しなくても閉じられて、Excelも終了するようになっています。

かなり経ってからの「発見」なので新しいバージョンの「仕様」なのか、もしくはWindowsUpdateでのアップデートによるものなのかは判りません。 いずれにしても「Application.Quit」以降のコードは実行されているので、この点に着目して以下をご覧下さい。

テストをしていただくのに、3種類のサンプルを用意しました。

Application.Quitの3種類のテスト
(画像をクリックすると、このサンプルがダウンロードできます)
テストしていただく内容は、以下の通りです。
Application.Quitのみ発行
②先にWorkbook.Closeさせてから、Application.Quitを発行
③先にApplication.Quitさせてから、Workbook.Closeを発行
このそれぞれが、画像のように「ボタン」で起動できるようになっています。

先にソースコードを紹介します。

'***************************************************************************************************
'   ※Application.Quitテスト                                        ThisWorkbook(Class)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'   [参照設定]
'   ・Microsoft Scripting Runtime
'   ・Windows Script Host Object Model
'***************************************************************************************************
' 変更日付 Rev   変更履歴内容---------------------------------------------------------------------->
' 17/11/07(1.0.0)新規作成
'***************************************************************************************************
Option Explicit
'===================================================================================================
Private Const g_cnsLogFolder As String = "LOG"
Private Const g_cnsLogFilename As String = "QuitTEST.log"

'***************************************************************************************************
'   ■■■ シート上のボタンからの起動処理 ■■■
'***************************************************************************************************
'* 処理名 :Quit_TEST1
'* 機能  :「CloseはしないままQuitさせます。」ボタン処理
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2017年11月07日
'* 作成者 :井上 治
'* 更新日 :2017年11月07日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub Quit_TEST1()
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST1(Start)")
    '-----------------------------------------------------------------------------------------------
    ' 保存確認を避けるため、保存済みにする
    ThisWorkbook.Saved = True
    ' 他にブックが開いていなければ、Excelを終了する
    If Workbooks.Count <= 1 Then Application.Quit
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST1(End)")
End Sub

'***************************************************************************************************
'* 処理名 :Quit_TEST2
'* 機能  :「CloseさせてからQuitさせます。」ボタン処理
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2017年11月07日
'* 作成者 :井上 治
'* 更新日 :2017年11月07日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub Quit_TEST2()
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST2(Start)")
    '-----------------------------------------------------------------------------------------------
    ' 保存確認を避けるため、保存済みにする
    ThisWorkbook.Saved = True
    ' 本ブックをClose
    ThisWorkbook.Close False
    ' 他にブックが開いていなければ、Excelを終了する
    If Workbooks.Count <= 1 Then Application.Quit
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST2(End)")
End Sub

'***************************************************************************************************
'* 処理名 :Quit_TEST3
'* 機能  :「QuitさせてからCloseさせます。」ボタン処理
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2017年11月07日
'* 作成者 :井上 治
'* 更新日 :2017年11月07日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub Quit_TEST3()
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST3(Start)")
    '-----------------------------------------------------------------------------------------------
    ' 保存確認を避けるため、保存済みにする
    ThisWorkbook.Saved = True
    ' 他にブックが開いていなければ、Excelを終了する
    If Workbooks.Count <= 1 Then Application.Quit
    ' 本ブックをClose
    ThisWorkbook.Close False
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST3(End)")
End Sub

'***************************************************************************************************
'   ■■■ 共通サブ処理 ■■■
'***************************************************************************************************
'* 処理名 :GP_OutputLog
'* 機能  :ログ出力
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :Arg1 = ログテキスト(String)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2017年11月07日
'* 作成者 :井上 治
'* 更新日 :2017年11月07日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Public Sub GP_OutputLog(ByVal strLogRec As String)
    '-----------------------------------------------------------------------------------------------
    Dim objFso As FileSystemObject                                  ' FileSystemObject
    Dim objTs As TextStream                                         ' TextStream
    Dim strFilename As String                                       ' ファイル名
    Dim strRec As String                                            ' レコードWORK
    Set objFso = New FileSystemObject
    ' ログファイル名取得
    strFilename = FP_GetLogFilename(objFso)
    ' ログの出力(追記)
    Set objTs = objFso.OpenTextFile(strFilename, ForAppending, True)
    strRec = Format(Now, "yyyy/MM/dd HH:mm:ss") & " " & strLogRec
    objTs.WriteLine strRec
    objTs.Close
    Set objTs = Nothing
    Set objFso = Nothing
End Sub

'***************************************************************************************************
'* 処理名 :FP_GetLogFilename
'* 機能  :ログファイル名取得
'---------------------------------------------------------------------------------------------------
'* 返り値 :ログファイル名(String)
'* 引数  :Arg1 = FileSystemObject(Object)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2017年11月07日
'* 作成者 :井上 治
'* 更新日 :2017年11月07日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Function FP_GetLogFilename(ByRef objFso As FileSystemObject) As String
    '-----------------------------------------------------------------------------------------------
    Dim strPathname As String                                       ' フォルダ名
    ' "LOG"フォルダ名(フルパス)の編集
    With New WshShell
        strPathname = objFso.BuildPath(.SpecialFolders("MyDocuments"), g_cnsLogFolder)
    End With
    ' "LOG"フォルダの作成
    If Not objFso.FolderExists(strPathname) Then
        objFso.CreateFolder strPathname
    End If
    ' "LOG"ファイル名(フルパス)の編集
    FP_GetLogFilename = objFso.BuildPath(strPathname, g_cnsLogFilename)
End Function

'------------------------------------------<< End of Source >>--------------------------------------
Quit_TEST1」「Quit_TEST2」「Quit_TEST3」が上の画像にある3個のボタンから起動されるプロシージャです。


'***************************************************************************************************
'   ※Application.Quitテスト                                        ThisWorkbook(Class)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
' 変更日付 Rev   変更履歴内容---------------------------------------------------------------------->
' 17/11/07(1.0.0)新規作成
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ ワークブックイベント ■■■
'***************************************************************************************************
'* 処理名 :Workbook_BeforeClose
'* 機能  :Close前イベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :Arg1 = キャンセル(Boolean)                         ※Ref参照
'---------------------------------------------------------------------------------------------------
'* 作成日 :2017年11月07日
'* 作成者 :井上 治
'* 更新日 :2017年11月07日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub Workbook_BeforeClose(Cancel As Boolean)
    '-----------------------------------------------------------------------------------------------
    Call GP_OutputLog("Workbook_BeforeClose")
End Sub

'------------------------------------------<< End of Source >>--------------------------------------
今回はワークブックが「閉じる」動作となった時に「Workbook_BeforeClose」イベントが動作するだろうということから、このイベントプロシージャも実装させてます。

今回のテストの着目点
一連の処理の終了段階だとしてマクロが動作している自身のワークブックを閉じて、他に開いているワークブックがなければExcelも終了させたいという時の動作を想定して下さい。 特に見ていただきたいのは「Application.Quit」「Workbook.Close」の後にソースコードがあったら実行されるのか、ということです。 また、ブックが閉じられる動作になれば「Workbook_BeforeClose」イベントが動作するはずなので、それも含めて検証してみます。

先頭の「コラム」のように画面上の操作で割り込んでしまうとイベント動作などはよく判らなくなってしまうので、今回はマイドキュメントの下の「LOG」フォルダに 「QuitTEST.log」というログファイルを出力させているので、このファイルで状態を確認して下さい。
なお、「Application.Quit」は開いているワークブックの数をカウントして行なっているので、 他のワークブックが開いていない状態で実行する必要があります。

CloseはしないままQuitさせます。」ボタンの処理(Quit_TEST1)
処理ログはこの通りでした。

2017/11/05 08:15:05 Quit_TEST1(Start)
2017/11/05 08:15:05 Quit_TEST1(End)
2017/11/05 08:15:05 Workbook_BeforeClose
結果的には「Workbook.Close」の記述がないのに開いているブックが閉じられてExcelが終了しています。 開いているブックが閉じられる段階での「Workbook_BeforeClose」イベントも動作しているのが判ります。
但し、ログに「Quit_TEST1(End)」があることで「Application.Quit」以降のコードが実行されているのが判ると思います。 つまり、「Application.Quit」の瞬間に終了しているわけではないということです。

CloseさせてからQuitさせます。」ボタンの処理(Quit_TEST2)
処理ログはこの通りでした。

2017/11/05 08:15:58 Quit_TEST2(Start)
2017/11/05 08:15:58 Workbook_BeforeClose
この処理ではワークブックは閉じられますが、Excelは終了せずに終わってしまいます。
つまり「Workbook.Close」でマクロ処理は終了してしまうということです。 処理ログに「Quit_TEST2(End)」がないので、以降のコードが実行されていないのは判りますが、「Application.Quit」へ進んだのかは判定できません。
Workbook.Close」と「Application.Quit」の間にもログをさせるようにすれば確認できると思います。

QuitさせてからCloseさせます。」ボタンの処理(Quit_TEST3)
処理ログはこの通りでした。

2017/11/05 08:16:28 Quit_TEST3(Start)
2017/11/05 08:16:28 Workbook_BeforeClose
この処理ではワークブックは閉じられて、Excelも終了します。
やはり「Application.Quit」以降のコードは実行されて、「Workbook.Close」で終了するのだろうと推測できます。

さて、よく判りませんが....
先頭のコラムに書いた通りですが、Excel2003までは「CloseはしないままQuitさせます。(Quit_TEST1)」では ワークブックも閉じないし、Excelも終了しなかったのです。
ですが、現在では「QuitさせてからCloseさせます。(Quit_TEST3)」とログ内容は違いますが、同じ結果になっっています。


'***************************************************************************************************
'* 処理名 :Quit_TEST1
'* 機能  :「CloseはしないままQuitさせます。」ボタン処理
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2017年11月07日
'* 作成者 :井上 治
'* 更新日 :2017年11月12日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub Quit_TEST1()
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST1(Start)")
    '-----------------------------------------------------------------------------------------------
    ' 保存確認を避けるため、保存済みにする
    ThisWorkbook.Saved = True
    ' 他にブックが開いていなければ、Excelを終了する
    If Workbooks.Count <= 1 Then Application.Quit
    '###############################################################################################TEST
    Stop
    '###############################################################################################TEST
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST1(End)")
End Sub
例えば、このように「Application.Quit」の直後に「Stop」を加えて、ここでコードの実行を止めてみます。
すると、どうなるでしょう。

2017/11/12 09:56:15 Quit_TEST1(Start)
2017/11/12 09:56:15 Workbook_BeforeClose
普通なら、「Stop」のところでコード実行が停止して、以後はステップ実行などができるはずなのですが、 停止はしないし、「Quit_TEST1(End)」のログも出力されませんが、「Workbook_BeforeClose」は実行されています。
どうやら「Application.Quit」以降は、通常のコードの実行は行なわれるものの、デバッグは許可されないのかも知れません。 現に先頭のコラムの状態では、「Application.Quit」の後のメッセージボックスは表示されています。

では、通常のコードで停止させてみます。VBAの動作を一定時間止める。」で紹介しているAPIの「スリープ(Sleep)」を使います。

' ■スリープ(API)
Private Declare Sub Sleep Lib "KERNEL32.dll" (ByVal dwMilliseconds As Long)
まず、モジュールレベルにこのAPIの宣言を追加しておきます。


'***************************************************************************************************
'* 処理名 :Quit_TEST1
'* 機能  :「CloseはしないままQuitさせます。」ボタン処理
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2017年11月07日
'* 作成者 :井上 治
'* 更新日 :2017年11月12日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub Quit_TEST1()
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST1(Start)")
    '-----------------------------------------------------------------------------------------------
    ' 保存確認を避けるため、保存済みにする
    ThisWorkbook.Saved = True
    ' 他にブックが開いていなければ、Excelを終了する
    If Workbooks.Count <= 1 Then Application.Quit
    '###############################################################################################TEST
    ' ログ出力
    Call GP_OutputLog("スリープで5秒停止します")
    ' スリープで5秒停止させてみる
    Sleep 5000
    ' ログ出力
    Call GP_OutputLog("スリープで5秒停止しました")
    '###############################################################################################TEST
    '-----------------------------------------------------------------------------------------------
    ' ログ出力
    Call GP_OutputLog("Quit_TEST1(End)")
End Sub
Quit_TEST1」の方は、さきほどの「Stop」のところをこのように書き換えます。
これで実行してみます。

2017/11/12 11:43:25 Quit_TEST1(Start)
2017/11/12 11:43:25 スリープで5秒停止します
2017/11/12 11:43:30 スリープで5秒停止しました
2017/11/12 11:43:30 Quit_TEST1(End)
2017/11/12 11:43:30 Workbook_BeforeClose
処理ログはこのようになり、5秒停止を含めて「Application.Quit」以降に書かれているコードが実行されることが判ると思います。
Stop」の説明と同じですが、「Application.Quit」より手前の行からデバッグでステップ実行を行なうと、 「Application.Quit」の行を越えるところでワークブックが閉じて終了してしまうので、 「Application.Quit」の行で瞬時に終了すると勘違いしてしまうのでご注意下さい。

ついでに説明しておきますが、「Workbook_BeforeClose」の引数「Cancel」は有効で、 「Workbook_BeforeClose」の中に「Cancel = True」の行を加えておくと 今回の3つのボタンは「何も働かない」というように見える動作になります。
ちなみに手動でもExcelが閉じられなくなるので、閉じる時は「Cancel = True」の行を消すか、 「デザインモード」にして閉じて下さい。