Excel.Application Object .Quit leaves EXCEL.EXE ru

2019-02-19 14:06发布

问题:

I'm working within MS Access 2013 on Windows 10 and I'm simply trying to write a Sub that opens an Excel file on disk, changes the column formatting and some columns, saves and quits. The Sub runs and does as intended, but the problem is that even after the .Quit command, "EXCEL.EXE" keeps running and subsequent calls to that Sub will result in run-time errors. If I close Access after running the sub, "EXCEL.EXE" disappears on the Task Manager, but not if I do "Compact and Repair" the database.

I've done one other Sub that simply opens an Excel file on disk and alters te width of all columns to "auto-width", then closes, which works fine and doesn't leave "EXCEL.EXE" running.

What am I doing wrong? Is it possible in VBA to make sure that "EXCEL.EXE" correctly quits?

I tried a lot of different line order on Dims and Sets to Nothing, Workbook Closes, etc etc. Also, I searched here and on other sites on how to solve this, but after 2h the only suggestions I've seen for VBA is using something like ThisWorkbook.Saved = True, but I tried that before and after .Quit to no effect. Other than that, I only find solutions for VB.NET or other environments that I'm not using and now next to nothing of.

Thanks for reading this.

Code follows:

Sub changeXLcolumnFormatting()

Dim XL As Object
Set XL = CreateObject("Excel.Application")
XL.Visible = False
XL.DisplayAlerts = False
XL.Workbooks.Open "C:\Users\640344\Desktop\rawDataTest.XLSX"
Dim sht As Worksheet

With XL
Set sht = ActiveWorkbook.Worksheets(1)

Dim rng As Range
Dim i As Integer, j As Integer

field_names = Split("datasistema|Data de Registo|Data Registo CMVM", "|")
sht.Select
end_of_table = sht.UsedRange.Columns.Count

For j = 0 To UBound(field_names)
    For i = 1 To end_of_table
        Set rng = sht.Cells(1, i)
        If InStr(rng.Text, field_names(j)) > 0 Then
            sht.Columns(i).NumberFormat = "yyyy-mm-dd  HH:MM:ss"
        End If
    Next i
Next j

End With

Set rng = Nothing
Set sht = Nothing
XL.ActiveWorkbook.Close (True)

XL.Quit
Set XL = Nothing

End Sub

回答1:

Declare and use a specific Workbook object - as you do for Worksheet and Range, like this:

    Dim xls     As Excel.Application
    Dim wkb     As Excel.Workbook
    Dim wks     As Excel.Worksheet
    Dim rng     As Range
    
    Set xls = New Excel.Application
    Set wkb = xls.Workbooks.Open("c:\test\workbook1.xlsx")
    Set wks = wkb.Worksheets(1)
    Set rng = wks.Range(<something>)
    
    ' Do stuff.
    wks.Name = "My New Name"
    With rng
         ' Do more.
    End With

    wkb.Close True
    
    Set rng = Nothing
    Set wks = Nothing
    Set wkb = Nothing
    
    xls.Quit
    
    Set xls = Nothing

Also, don't use Select, that's for visible use only. Define ranges instead.


Cinetyk's EDIT:

Using @Gustav 's indications, the code that does what I wanted and solves the problem is:

Sub changeXLcolumnFormatting()

Dim XL As Excel.Application
Dim sht As Excel.Worksheet
Dim wkb As Excel.Workbook
Dim rng As Range

Set XL = New Excel.Application
XL.Visible = False
XL.DisplayAlerts = False

Set wkb = XL.Workbooks.Open("C:\Users\640344\Desktop\rawDataTest.XLSX")
Set sht = wkb.Worksheets(1)

Dim i As Integer, j As Integer

field_names = Split("datasistema|Data de Registo|Data Registo CMVM", "|")
end_of_table = sht.UsedRange.Columns.Count

For j = 0 To UBound(field_names)
    For i = 1 To end_of_table
        Set rng = sht.Cells(1, i)
        If InStr(rng.Text, field_names(j)) > 0 Then
            sht.Columns(i).NumberFormat = "yyyy-mm-dd  HH:MM:ss"
        End If
    Next i
Next j

wkb.Close (True)
Set rng = Nothing
Set sht = Nothing

XL.Quit
Set XL = Nothing

End Sub


回答2:

Here is a fancy way to solve that problem - using with new Excel.Application:

Option Compare Database
Option Explicit

Public Sub TestMe()

    Dim wkb     As Object
    Dim wks     As Object

    With New Excel.Application

        Set wkb = .Workbooks.Open("C:\Users\vityata\Desktop\myTest.xlsx")
        Set wks = wkb.Worksheets(1)

        wkb.Close True

        Set wks = Nothing
        Set wkb = Nothing
        .Quit

    End With

End Sub

In C# this is a standard with Using, in VBA very few people use it - I have never seen it production code.


Cinetyk's EDIT:

Using Vityata's indications, the code that works as I intended is:

Option Compare Database
Option Explicit

Public Sub changeXLcolumnFormattingV2()

Dim sht As Object
Dim wkb As Object

With New Excel.Application
    .Visible = False
    .DisplayAlerts = False

    Set wkb = .Workbooks.Open("C:\Users\640344\Desktop\rawDataTESTING.XLSX")
    Set wks = wkb.Worksheets(1)

    field_names = Split("datasistema|Data de Registo|Data Registo CMVM", "|")
    end_of_table = wks.UsedRange.Columns.Count

    For j = 0 To UBound(field_names)
        For i = 1 To end_of_table
            Set rng = wks.Cells(1, i)
            If InStr(rng.Text, field_names(j)) > 0 Then
                wks.Columns(i).NumberFormat = "yyyy-mm-dd  HH:MM:ss"
            End If
        Next i
    Next j

    wkb.Close True

    Set wks = Nothing
    Set wkb = Nothing
    .Quit

End With

End Sub