How to detect when a workbook is closing?

后端 未结 6 2360
后悔当初
后悔当初 2021-02-15 13:12

The Workbook.BeforeClose event triggers when the workbook is about to close but before the saving message prompt which allows cancelling it.

How can I detect when the wo

6条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2021-02-15 14:06

    In order to get around your edge case, you need to handle the case where the workbook is deactivated within 1 second of closing it, but only when the save prompt was displayed.

    To check if less than 1 second has elapsed, use a high resolution timer to store the time in the Workbook_BeforeClose event, and then compare against it in the Workbook_Deactivate event. Assuming that clsTimer is a suitable high res timer, your code should now be:

    Private MyTimer As clsTimer
    Private StartTime As Currency
    
    Private Sub Workbook_BeforeClose(ByRef Cancel As Boolean)
    
        closing_event = True
        Set MyTimer = New clsTimer
        StartTime = MyTimer.MicroTimer
        check_time = VBA.Now + VBA.TimeSerial(Hour:=0, Minute:=0, Second:=1)
        Excel.Application.OnTime EarliestTime:=check_time, Procedure:="disable_closing_event"
    
    End Sub
    
    Private Sub Workbook_Deactivate()
    
        If closing_event Then
    
            If Not ThisWorkbook.Saved Then
                'The Save prompt must have been displayed, and the user clicked No or Cancel or pressed Escape
    
                If MyTimer.MicroTimer - StartTime < 1 Then
                    'The user must have pressed Escape and Alt-Tabbed
                    closing_event = False
                Else
                    'Your Windows API calls here
                End If
            Else
                'The workbook was saved before the close event, so the Save prompt was not displayed
                'Your Windows API calls here
            End If
            Excel.Application.OnTime EarliestTime:=check_time, Procedure:="disable_closing_event", Schedule:=False
        End If
    
        Set MyTimer = Nothing
    
    End Sub
    

    The class module for clsTimer looks like this:

    Private Declare PtrSafe Function getFrequency Lib "kernel32" _
    Alias "QueryPerformanceFrequency" (cyFrequency As Currency) As Long
    
    Private Declare PtrSafe Function getTickCount Lib "kernel32" _
    Alias "QueryPerformanceCounter" (cyTickCount As Currency) As Long
    
    
    Public Function MicroTimer() As Currency
    
        ' Returns seconds.
    
        Dim cyTicks1 As Currency
        Static cyFrequency As Currency
    
        MicroTimer = 0
    
        ' Get frequency.
        If cyFrequency = 0 Then getFrequency cyFrequency
    
        ' Get ticks.
        getTickCount cyTicks1
    
        ' Seconds
        If cyFrequency Then MicroTimer = cyTicks1 / cyFrequency
    
    End Function
    

提交回复
热议问题