Timing or other issue when closing Excel from Access (1 Viewer)

sxschech

Registered User.
Local time
Today, 06:08
Joined
Mar 2, 2010
Messages
805
I thought this code was working properly, however, yesterday noticed that the excel file was showing as locked and excel wasn't open (from the perspective of the taskbar not having an underline below the icon). When I went into Task Manager, Excel was there under Background Processes. I think the reason I hadn't encountered previously was that I usually had another excel file open and the code is instructed not to close excel if other files are open. I found when I stepped through the code, Excel closed properly. Leading me to think it was a timing issue. I tried do events and made a visit to Mr. GPT and we had a dialogue with suggestions to use timer and loop which were unsuccessful. The only method I got to work other than stepping through the code was via MsgBox. Once the message box became visible, I could see the lock file disappear as well as Excel no longer showing up in the Background Process.

Are there other suggestions to sort this without the need to click a message box or other form?

Code:
Function ExportPDF(xlFileName As String, SavePDF As String, Optional DisplayPDF As Boolean)
'Code to save xlsx as pdf
'Note: If file exists, it is automatically replaced
'https://access-programmers.co.uk/forums/showthread.php?t=228641
'2018114
    Dim objXl As Object
    
    Const xlTypePDF = 0
    Const xlQualityStandard = 0
    
    ' Establish an EXCEL application object
    On Error Resume Next
    Set objXl = GetObject(, "Excel.Application")
    If Err.Number <> 0 Then
        Set objXl = CreateObject("Excel.Application")
    End If
    Err.Clear
    On Error GoTo 0
    
    With objXl
        .Workbooks.Open (xlFileName)
          
        .ActiveWorkbook.ExportAsFixedFormat Type:=xlTypePDF, FileName:= _
            "" & SavePDF & "", quality:=xlQualityStandard, _
            includedocproperties:=True, ignoreprintareas:=False, openafterpublish:= _
            DisplayPDF
        '.Quit
    End With
    'https://www.mrexcel.com/board/threads/quit-current-workbook-but-not-other-active-workbooks.1088914/
    '20240531
    DoEvents
    
    If objXl.Workbooks.Count = 1 Then
        objXl.Quit
        Set objXl = Nothing
        ' Give Excel more time to fully release the resources
        ' Couldn't figure out how to fully release excel using
        ' doevents or loop so ended up with a msgbox
        ' 20250116
        MsgBox "Since there are no other workbooks open, Closing Excel", vbInformation + vbOKOnly, "Timing Issue on Close"
        
    Else
        objXl.ActiveWorkbook.Close
        DoEvents
    End If
End Function
 
I would just open another Excel instance and then close that and set to nothing.
 
I noted that you open the workbooks but don't close them before you attempt the .Quit, and yet you KNOW that the workbook IS open here...

Code:
    If objXl.Workbooks.Count = 1 Then
        objXl.Quit
        Set objXl = Nothing<br>

Just for snorts and giggles I would try to close the workbook before you quit the app. Whether you attempt to save or abandon any changes is up to you. This is a guess, but "dangling" Excel instances is not an unknown problem.
 
Made some changes and added the Close statement. The timing issue seems to have moved up the chain...
...was able to get rid of the message box, however had to repeat the Set Statement to avoid an error on wb.Activate, probably also relating to the timing since the second one doesn't generate an error and the rest of the code completes.

Run-time error '-2147417848 (80010108)'
Automation error
The object invoked has disconnected from its clients

Code:
Function ExportPDF(xlFileName As String, SavePDF As String, Optional DisplayPDF As Boolean)
'Code to save xlsx as pdf
'Note: If file exists, it is automatically replaced
'https://access-programmers.co.uk/forums/showthread.php?t=228641
'2018114
'Modified for closing file and releasing lock when
'only one file open
'20250117
    Dim objXl As Object
    Dim wb As Object
    Const xlTypePDF = 0
    Const xlQualityStandard = 0
    
    ' Establish an EXCEL application object
    On Error Resume Next
    Set objXl = GetObject(, "Excel.Application")
    If Err.Number <> 0 Then
        Set objXl = CreateObject("Excel.Application")
    End If
    Err.Clear
    On Error GoTo 0
    
    objXl.Visible = True
    Set wb = objXl.Workbooks.Open(xlFileName, True, False)
    DoEvents
    'Second set statement to overcome automation error (timing issue)
    Set wb = objXl.Workbooks.Open(xlFileName, True, False)
    wb.Activate
    wb.ExportAsFixedFormat Type:=xlTypePDF, FileName:= _
            "" & SavePDF & "", quality:=xlQualityStandard, _
            includedocproperties:=True, ignoreprintareas:=False, openafterpublish:= _
            DisplayPDF
    'https://www.mrexcel.com/board/threads/quit-current-workbook-but-not-other-active-workbooks.1088914/
    '20240531
    DoEvents
    
    If objXl.Workbooks.Count = 1 Then
        wb.Close
        objXl.Quit
        Set wb = Nothing
        Set objXl = Nothing
    Else
        wb.Close
        Set wb = Nothing
    End If
End Function
 
The error in question says that the object you tried to use via Component Object Model techniques is now a de-referenced object i.e. if you examined it with the debugger, it would say "Nothing" (which is the object equivalent of Null).
 
Code:
' ...
    wb.ExportAsFixedFormat Type:=xlTypePDF, FileName:= _
            "" & SavePDF & "", quality:=xlQualityStandard, _
            includedocproperties:=True, ignoreprintareas:=False, openafterpublish:= _
            DisplayPDF
' ...
If DisplayPDF is True, then it is likely Excel will remain as a running task until the PDF is closed (since it is opened by the Excel process). Closing the PDF file *should* allow the Excel process to terminate. (I haven't tested this!)

You could alternatively, always pass False to the DisplayPDF parameter, and open the PDF from Access in the next line of code. Then, when you Quit objXL, it should do so cleanly.
 
I wonder about this:

Code:
    If objXl.Workbooks.Count = 1 Then
        wb.Close
        objXl.Quit
        Set wb = Nothing
        Set objXl = Nothing
   Else
        wb.Close
        Set wb = Nothing
    End If

Can objXL.Workbooks.Count ever be > 1? Object wb will always be either defined and pointing to an open workbook, or not defined and pointing to Nothing. It is a single object and thus is ALWAYS singular. But objXL.Workbooks can legally be plural.
 
you can also create an instance of excel and leave it open while your db is open.
it will get closed when you close your database.

Code:
Function ExportPDF(xlFileName As String, SavePDF As String, Optional DisplayPDF As Boolean)
'Code to save xlsx as pdf
'Note: If file exists, it is automatically replaced
'https://access-programmers.co.uk/forums/showthread.php?t=228641
'2018114
'Modified for closing file and releasing lock when
'only one file open
'20250117
    Static objXl As Object
    Dim wb As Object
    Const xlTypePDF = 0
    Const xlQualityStandard = 0
    
    ' Establish an EXCEL application object
    On Error Resume Next
    ' arnelgp
    ' 01/18/2025
    ' just create an instance of excel
    ' and leave it open
    ' don't worry, when you close the
    ' db it will also get closed
    If objXl Is Nothing Then
        Set objXl = CreateObject("Excel.Application")
    End If
    
    ' why do you need to make it visible?
    objXl.Visible = True
    Set wb = objXl.Workbooks.Open(xlFileName, True, False)
    DoEvents
    'Second set statement to overcome automation error (timing issue)
    Set wb = objXl.Workbooks.Open(xlFileName, True, False)
    wb.Activate
    wb.ExportAsFixedFormat Type:=xlTypePDF, FileName:= _
            "" & SavePDF & "", quality:=xlQualityStandard, _
            includedocproperties:=True, ignoreprintareas:=False, openafterpublish:= _
            DisplayPDF
    'https://www.mrexcel.com/board/threads/quit-current-workbook-but-not-other-active-workbooks.1088914/
    '20240531
    DoEvents
    wb.Close
    Set wb = Nothing
    
    'arnelgp
    'i hid it again here
    objXl.Visible = False
End Function
 
If Excel was already open and objXL is set by the .GetObject, then yes (if there is already a workbook open in Excel)

Having an unsaved object still open can prevent Excel from quitting when you tell it to do so. You can do something similar to

Code:
DIM WB As Excel.WorkBook
...

For Each WB in objXL.Workbooks
    WB.Close SaveChanges := False
Next WB
objXl.Quit

This closes every workbook and Excel itself (in that order). If there is the odd chance that you will have some workbook open from another app and cannot shut that down from this instance of Access, then you have to have a way to know that the last Excel-using App has a clear field to close everything and you can't allow yourself to be bothered by having Excel still open.
 

Users who are viewing this thread

Back
Top Bottom