0

The excel.exe*32 process remains open in the Windows Task Manager even though I am closing it in VBA. I am running the following VBA code from Access. I have looked and tried various solutions to no avail. The only way to close excel.exe is to quit Access. Can someone point out what I am missing.

Public Sub GenerateQualityReportsSub()


On Error GoTo ERR_GenerateQualityReportsSub
Dim xl As Excel.Application
Dim wbk As Excel.Workbook
Dim dbs As DAO.Database
Dim rstRpt As DAO.Recordset
Dim objMyRange As Object
Dim rstList As DAO.Recordset
Dim FullOutFileName As String
Dim strSQLList As String
Dim strSQLRpt As String
Dim i As Integer
Dim DiscrepancyRecords As Long
Dim NeedToCloseExcel As Boolean
Dim ReportName As String
Dim col As Integer

'Initialize Variables
Set dbs = CurrentDb
RunDate = Now()
FullOutFileName = "DataQualityDiscrepancyReport.xlsx"
i = 0
DiscrepancyRecords = 0
NeedToCloseExcel = False

'Determine the Reports to Generate
strSQLList = "" & _
  "SELECT ReportNum, ReportName, SheetName, QueryName, [Responsible Department] " & _
  "FROM [Data Quality Reports] " & _
  "ORDER BY ReportNum"
Set rstList = dbs.OpenRecordset(strSQLList, dbOpenSnapshot, dbReadOnly)
If rstList.RecordCount = 0 Then
    i = 0
    GoTo Exit_GenerateQualityReportsSub
Else
    'Open Excel
    Set xl = New Excel.Application                      'Open the Excel File
    xl.Visible = True                                 'Make Excel Invisible to User
    'Create the Excel Spreadsheet and Sheets
    Set wbk = xl.Workbooks.Add                          'Add a Wookbook to the Excel File
    wbk.Sheets("Sheet1").Select                             'Select Sheet 1
    wbk.SaveAs FileName:=FullOutFileName                'Save the Excel File
    NeedToCloseExcel = True
End If

'Create One Sheet Per Report
i = 1
While Not rstList.EOF
    DiscrepancyRecords = 0

'Add, if necessary, and Rename the Sheet
    If i <> 1 Then
        Set wks = xl.Worksheets.Add                           'Add a Wooksheet to the Excel File
    End If
    wbk.Sheets("Sheet" & i).Select                            'Select the new Sheet
    wbk.Sheets("Sheet" & i).Name = rstList("SheetName")       'Rename the Sheet
    Set wks = wbk.activesheet


'Obtain and Write Data to the Excel Sheet
    strSQLRpt = "Select * from [" & rstList("QueryName") & "]"
    Set objMyRange = wks.Cells(xl.activesheet.UsedRange.Rows.Count + 1, 1)
    Set rstRpt = dbs.OpenRecordset(strSQLRpt, dbOpenSnapshot, dbReadOnly)
    If rstRpt.RecordCount = 0 Then
        GoTo Exit_GenerateQualityReportsSub
    Else
        rstRpt.MoveLast
        DiscrepancyRecords = rstRpt.RecordCount
        rstRpt.MoveFirst
    End If

'Write the Column Headers to the Sheet
    For col = 0 To rstRpt.Fields.Count - 1
        wks.Cells(1, col + 1) = rstRpt.Fields(col).Name
    Next col

'Write Data to the Excel Sheeet
    Range("A2").Select
    With objMyRange
        rstRpt.MoveFirst
        .CopyFromRecordset rstRpt
    End With

'Format the Sheet Cells
    Cells.Select
    Selection.Columns.AutoFit
    Range("A1").Select

'Save the Excel File
    wbk.Save                                                'Save the Excel File


NextReport:
'Close the Data Results
    rstRpt.Close
    Set rstRpt = Nothing
    rstList.MoveNext
    i = i + 1
Wend
i = i - 1

'Close the Excel File and Application
xl.Visible = True
wbk.Save
wbk.Close savechanges:=True
xl.Quit
Set wks = Nothing
DoEvents
Set wbk = Nothing
DoEvents
Set xl = Nothing
DoEvents
NeedToCloseExcel = False

'Close the Report Record
rstList.Close
Set rstList = Nothing

Exit_GenerateQualityReportsSub:
If NeedToCloseExcel Then
    xl.Visible = True
    wbk.Save
    wbk.Close savechanges:=True
    xl.Quit
    Set wks = Nothing
    DoEvents
    Set wbk = Nothing
    DoEvents
    Set xl = Nothing
    DoEvents
    NeedToCloseExcel = False
End If
Exit Sub


ERR_GenerateQualityReportsSub:
.....

End Sub
Cœur
  • 37,241
  • 25
  • 195
  • 267
VBAConst
  • 1
  • 1
  • 2
  • Please format your post as a code block (four spaces in front of each line). – Halvor Holsten Strand Aug 04 '14 at 16:09
  • Fixed formatting as requested – VBAConst Aug 04 '14 at 16:21
  • Excellent. Now, do you have any ideas why this is happening? This is quite a lot of code to browse through. – Halvor Holsten Strand Aug 04 '14 at 16:23
  • Reading prior posts they mention leaving an Excel object referenced by VBA when the Subroutine closes. I applied all the suggestions including using DoEvents yet Excel.exe*32 still remain open in the Task Manager. I need to make sure that I am referencing and closing all the Excel objects properly with the code. If I am then there must be another issue that is leaving execl.exe*32 open. – VBAConst Aug 04 '14 at 16:34
  • I reviewed the answer to the possible duplicate mentioned above and the suggestions recommended are already implemented in my code yet excel.exe*32 still remains active in Task Manager. – VBAConst Aug 04 '14 at 18:58
  • ActiveX objects must shut down in the following way: If the object's application is visible, the object should shut down only in response to an explicit user command (for example, clicking Exit on the File menu) or the equivalent command from an ActiveX client. If the object's application is not visible, the object should shut down only when the last external reference is gone. – Noodles Aug 04 '14 at 19:00
  • If the object's application is visible and is controlled by an ActiveX client, it should become invisible when the user shuts it down (for example, clicking Exit on the File menu). This behavior allows the controller to continue to control the object. The controller should shut down only when the last external reference to the object has disappeared. – Noodles Aug 04 '14 at 19:00
  • Therefore shouldn't the following shut down Excel and remove it form the Task Manager:xl.Visible = True xl.Visible = True wbk.Save wbk.Close savechanges:=True xl.Quit Set wks = Nothing DoEvents Set wbk = Nothing DoEvents Set xl = Nothing DoEvents – VBAConst Aug 04 '14 at 19:30
  • 1
    In light of @Noodles comment, you might try changing `xl.Visible = True` in the last If statement to `xl.Visible = False` or comment it out if you are sure it is already invisible. Make sure when you start your test that you end any existing Excel processes or you will be unable to tell if it is your code or just the pre-existing Excel process. – Blackhawk Aug 04 '14 at 19:32
  • changed xl.Visible to False yet it still leaves excel,exe*32 open in the Task Manager 'Close the Excel File and Application xl.Visible = False wbk.Save wbk.Close savechanges:=True xl.Quit Set wks = Nothing DoEvents Set wbk = Nothing DoEvents Set xl = Nothing DoEvents – VBAConst Aug 04 '14 at 19:41
  • I've experienced this a lot. Make the recommended changes from the other answer, then make sure you don't have any remaining instances of Excel running. Then run your code. I am willing to bet the issue disappears. Also remember that if you press `cntl+break` the clean up code will not execute and you will be left with an open instance. – RubberDuck Aug 04 '14 at 20:33

1 Answers1

2

What I would recommend:

Don't create a new Excel application, try to re-use an existing one first. Make sure you set the variables you used for manipulating Excel objects to Nothing before you quit the Excel app. In your code, you Quit the app, but still keep references to some of the variables.

'-----------------------------------------------------------------------------
' Return an intance of Excel
' First tries to open an existing instance. If it fails, it will create an instance.
' If that fails too, then we return 'Nothing'
'-----------------------------------------------------------------------------
Public Function GetExcelObject() As Object
    On Error Resume Next
    Dim xlo As Object
    ' Try to get running instance of Excel
    Set xlo = GetObject("Excel.Application")
    If xlo Is Nothing Then
        Set xlo = CreateObject("Excel.Application")
    End If
    Set GetExcelObject = xlo
End Function

then use:

Set xl = GetExcelObject()

When you're finished with your Excel file:

' Clear all variables that were used to contain Excel objects
set objMyRange = nothing
set Range = nothing
set Selection = nothing
' Save and close
wbk.Save
wbk.Close savechanges:=True
Set wks = Nothing
Set wbk = Nothing
xl.Quit
Set xl = Nothing

I think you need to revise your code a bit. Maybe you didn't include all of it here but there are some iffy things:

  • you use undeclared variables (Selection, Cell, Range). Maybe you didn't include the code that declares them but make sure you have Option Explicit at the top of all your VBA files to force you to declare all variables.

  • You have some GoTo Exit_GenerateQualityReportsSub which generally is a sign that your code needs refactoring. Apart from error managagement in VBA, it's extremely rare that you would need to use GoTo. In this case, you can perfectly use Exit Do to exit you loop.
    These jumps make reasoning about the code harder.

  • You also repeated the same cleanup code at the end of your function to take care of the GoTo branching. By duplicating your code, you take the risk of making it less maintainable and you may introduce errors by forgetting to update both blocks when you make a change to one.

Renaud Bompuis
  • 16,596
  • 4
  • 56
  • 86