SOLUTION: Access 2007 VBA to close Excel process (1 Viewer)

omnialive

Registered User.
Local time
Today, 15:37
Joined
Sep 17, 2010
Messages
23
I had struggled long and hard with finding a way to properly close out the EXCEL.EXE processes that were being "orphaned" when my function would finish its business. I had done all of the "XlApp.Quit" and "set XlApp = Nothing" and it still wasn't working.

I finally found out an answer that successfully closes the excel instance that is created:

Code:
Exit_insLoadFormData_Click:
''close everything up
xlApp.ActiveWorkbook.Close
xlApp.Quit

Set xlSheet = Nothing
DoEvents
Set xlBook = Nothing
DoEvents
Set xlApp = Nothing
DoEvents
    Exit Sub

The key are the DoEvents statements. No other solution I've read and tried has worked. This might be how I am initializing xlApp, et al:

Code:
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet

Set xlApp = New Excel.Application
Set xlBook = xlApp.Workbooks.Open(vrtSelectedItem)

''process Facility Info tab
Set xlSheet = xlBook.Worksheets("Facility Info")

Since I had found an answer to a problem that was plaguing me, I felt compelled to post it here. Any comments are welcome as I am still very new to VBA and Access.
 

boblarson

Smeghead
Local time
Today, 15:37
Joined
Jan 12, 2001
Messages
32,059
Interesting; I've never had to use DoEvents. The usual culprits are probably forgetting the Quit and this:

http://blogs.msdn.com/b/access/arch...-objects-when-referring-to-excel-in-code.aspx

I've never had that problem either. It would be interesting to see the ENTIRE bit of code which was having the problem. I'll bet that I could find one spot where an instantiated object was not referenced and caused the extra Excel.exe to be in the task manager. But, perhaps not. Cool to see you refer to my article Paul. I was hoping that it might help people (I also have it in my Quick Tutorials on my website).

:)
 

pbaldy

Wino Moderator
Staff member
Local time
Today, 15:37
Joined
Aug 30, 2003
Messages
36,125
No problemo Bob; it's a good article.
 

omnialive

Registered User.
Local time
Today, 15:37
Joined
Sep 17, 2010
Messages
23
For those interested, here is the full section of code that was causing this. As an FYI, the standard of the coding isn't what I normally like. This is a rushed project that I am not happy about having dumped in my lap and I haven't done any software development like this in years. Mostly just scripting and have never worked with Access or Visual Basic before 3 weeks ago.

Also, I know that the file dialog I am using allows multiple files to be selected, but I've only been selecting one up to this point.

Code:
Private Sub insLoadFormData_Click()
 'On Error GoTo Err_insLoadFormData_Click

'show that we are doing work
DoCmd.Hourglass True

'Declare a variable as a FileDialog object.
    Dim fd As FileDialog

'boolean to determine if we canceled out of the filepicker dialog
    Dim boolFileCancel As Boolean
    boolFileCancel = False

    'Create a FileDialog object as a File Picker dialog box.
    Set fd = Application.FileDialog(msoFileDialogFilePicker)

    'Declare a variable to contain the path
    'of each selected item. Even though the path is a String,
    'the variable must be a Variant because For Each...Next
    'routines only work with Variants and Objects.
    Dim vrtSelectedItem As Variant

    'Use a With...End With block to reference the FileDialog object.
    With fd

        'Use the Show method to display the File Picker dialog box and return the user's action.
        'The user pressed the action button.
        If .Show = -1 Then

''setup to work with excel
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet

Set xlApp = New Excel.Application
            
            'Step through each string in the FileDialogSelectedItems collection.
            For Each vrtSelectedItem In .SelectedItems

Set xlBook = xlApp.Workbooks.Open(vrtSelectedItem)

''GET SHEET LIST
''this gets the list of excel worksheets to process
Dim strSheetList As String
strSheetList = DLookup("[CONFIG_VALUE]", "[CONFIG]", _
                       "[CONFIG_TYPE] = ""LOADFORM"" AND [CONFIG_SUBTYPE] = ""STRUCTURE"" AND [CONFIG_NAME] = ""SHEETLIST""")

Dim arrSheetList() As String
arrSheetList = Split(strSheetList, ",")
''GET SHEET LIST

''now loop through the list of worksheets, open them and process them accordingly
Dim intSheetCnt As Integer

For intSheetCnt = LBound(arrSheetList) To UBound(arrSheetList)

''process Facility Info tab
Set xlSheet = xlBook.Worksheets(arrSheetList(intSheetCnt))

  Dim stStartCol As String
  Dim intStartCol As Integer
  
  Dim stEndCol As String
  Dim intEndCol As Integer
  
  Dim stStartRow As String
  Dim intStartRow As Integer
  
  Dim stValidationCol As String
  Dim intValidationCol As Integer
  
  stStartCol = DLookup("[CONFIG_VALUE]", "[CONFIG]", _
   "[CONFIG_TYPE] = ""LOADFORM"" AND [CONFIG_SUBTYPE] = """ & UCase(arrSheetList(intSheetCnt)) & """ AND [CONFIG_NAME] = ""STARTCOL""")
  stEndCol = DLookup("[CONFIG_VALUE]", "[CONFIG]", _
   "[CONFIG_TYPE] = ""LOADFORM"" AND [CONFIG_SUBTYPE] = """ & UCase(arrSheetList(intSheetCnt)) & """ AND [CONFIG_NAME] = ""ENDCOL""")
  stStartRow = DLookup("[CONFIG_VALUE]", "[CONFIG]", _
   "[CONFIG_TYPE] = ""LOADFORM"" AND [CONFIG_SUBTYPE] = """ & UCase(arrSheetList(intSheetCnt)) & """ AND [CONFIG_NAME] = ""STARTROW""")
  stValidationCol = DLookup("[CONFIG_VALUE]", "[CONFIG]", _
   "[CONFIG_TYPE] = ""LOADFORM"" AND [CONFIG_SUBTYPE] = """ & UCase(arrSheetList(intSheetCnt)) & """ AND [CONFIG_NAME] = ""VALIDATIONCOL""")
               
  'MsgBox "startcol: " & stStartCol & " endcol: " & stEndCol & " startrow: " & stStartRow
  
  intStartCol = Asc(UCase(stStartCol))
  intEndCol = Asc(UCase(stEndCol))
  intStartRow = CInt(stStartRow)
  intValidationCol = Asc(UCase(stValidationCol))
  
  'MsgBox "intstartcol: " & intStartCol & " intendcol: " & intEndCol & " intstartrow: " & intStartRow
    
  Dim intCurRow As Integer
  Dim intColCnt As Integer
  Dim intCurCol As Integer
  Dim stRange As String
      
  intCurRow = intStartRow
  
  Dim boolQuit As Boolean
  boolQuit = False
  
  Dim gotheaders As Boolean
  gotheaders = False
  
  ReDim headers(intEndCol) As String
  ReDim values(intEndCol) As String

'If Not FileLocked("c:\text.txt") Then
' Open "c:\text.txt" For Output As #1
'End If


'Dim strLine As String
'Print #1, "===Worksheet: " & arrSheetList(intSheetCnt)
'Print #1, ""

  Do While Not boolQuit
  
    For intColCnt = intStartCol To intEndCol
       stRange = Chr(intColCnt) & CStr(intCurRow)
       
       If (Not gotheaders) Then
        headers(intColCnt) = xlSheet.Range(stRange).Value
'        strLine = strLine & xlSheet.Range(stRange).Value & ","
       Else
        values(intColCnt) = xlSheet.Range(stRange).Value
'        strLine = strLine & xlSheet.Range(stRange).Value & ","
       End If
       
    Next intColCnt
    ''do code to validate, generate and insert data into tables
    '' validation includes checking to see if we need to quit this loop!
    If (Not gotheaders) Then
     gotheaders = True
'Print #1, strLine
    Else
     If (values(intValidationCol) <> "") Then
'Print #1, strLine
     ''this is where we massage data before INSERT
     
     ''doing static sheet names until i can figure out a way to programmatically do this
     ''the Order-Result Info sheet currently proves a greater challenge beyond allotted time to overcome
     Dim boolRETURN As Boolean
     
      Select Case UCase(arrSheetList(intSheetCnt))
      Case "FACILITY INFO"
       ''insert entire row as is for now
       boolRETURN = InsertInfoRow(intStartCol, intEndCol, arrSheetList(intSheetCnt), headers(), values())
      Case "PATIENT INFO"
       ''1. generate new MRN for patient
        Dim newMRN As String
        newMRN = CStr(NextID("MRN"))
       ''2. update values() array with MRN info
        boolRETURN = updInfoArrayCol("MRN", newMRN, headers(), values())
       ''3. generate new PatientID for patient
        Dim newPatientID As String
        newPatientID = CStr(NextID("PATIENT"))
       ''4. make link between generated PatientID and PatientID in load form for order/result linking later
       ''3. insert row
      Case "ORDER-RESULT INFO"
      
      End Select

     Else
      boolQuit = True
     End If
    End If
    
'    Print #1, ""
'    Print #1, ""
'    strLine = ""
    
    ''go to the next row
     intCurRow = intCurRow + 1
   Loop
  
  
''for loop through next arrSheetList
Next intSheetCnt
  
            ''go through for loop for next selected file if multiple files selected
            Next vrtSelectedItem
        'The user pressed Cancel.
        Else
        boolFileCancel = True
        End If
    End With


    'Set the object variable to Nothing.
    Set fd = Nothing

'Close #1

Exit_insLoadFormData_Click:

If Not boolFileCancel Then
''don't run these cmds if they cancelled the file dialog
''close everything up
 xlApp.ActiveWorkbook.Close
 xlApp.Quit

 Set xlSheet = Nothing
 DoEvents
 Set xlBook = Nothing
 DoEvents
 Set xlApp = Nothing
 DoEvents

End If

'show that we are done working
DoCmd.Hourglass False

    Exit Sub

Err_insLoadFormData_Click:
    MsgBox "insLoadFormData Error: " & vbCrLf & Err.Number & ": " & Err.Description
    AddLogEntry "insLoadFormData Error: " & vbCrLf & Err.Number & ": " & Err.Description, "ERROR"
    Resume Exit_insLoadFormData_Click
 
End Sub
 

boblarson

Smeghead
Local time
Today, 15:37
Joined
Jan 12, 2001
Messages
32,059
For those interested, here is the full section of code that was causing this. As an FYI, the standard of the coding isn't what I normally like. This is a rushed project that I am not happy about having dumped in my lap and I haven't done any software development like this in years. Mostly just scripting and have never worked with Access or Visual Basic before 3 weeks ago.

It could have been with the InsertInfoRow function but I can't tell because you didn't post it. The rest of the code in this procedure has no code not attached to an instantiated object.
 

omnialive

Registered User.
Local time
Today, 15:37
Joined
Sep 17, 2010
Messages
23
Here is the InsertInfoRow code. I don't call or instantiate anything with the excel piece. Just process some arrays and insert into a table:

Code:
Public Function InsertInfoRow(intStart As Integer, intStop As Integer, strTableName As String, arrHeaders() As String, arrRow() As String) As Boolean
On Error GoTo Err_InsertInfoRow

 ''inserts a row into a table given a start, stop array range used on Headers and Row arrays to insert data into TableName
 Dim intCnt As Integer
 
 Dim strInsertSQL As String
 strInsertSQL = "INSERT INTO [" & strTableName & "] ("
 
 Dim strInsertSQLValues As String
 strInsertSQLValues = "VALUES ("
 
 For intCnt = intStart To intStop
  strInsertSQL = strInsertSQL & "[" & arrHeaders(intCnt) & "]"
  
  strInsertSQLValues = strInsertSQLValues & """" & arrRow(intCnt) & """"
  
  If intCnt <> intStop Then
   strInsertSQL = strInsertSQL & ","
   strInsertSQLValues = strInsertSQLValues & ","
  End If
  
 Next intCnt
 
 strInsertSQL = strInsertSQL & ") " & strInsertSQLValues & ");"
 
   DoCmd.SetWarnings False
 DoCmd.RunSQL strInsertSQL
   DoCmd.SetWarnings True
   
  InsertInfoRow = True

Exit_InsertInfoRow:
  Exit Function

Err_InsertInfoRow:
    Dim strErrorDescription As String
    strErrorDescription = "InsertInfoRow Error: " & vbCrLf & Err.Number & ": " & Err.Description _
            & vbCrLf & "SQL:" & vbCrLf & strInsertSQL
    
    MsgBox strErrorDescription
    AddLogEntry strErrorDescription, "ERROR"
    Resume Exit_InsertInfoRow


End Function
 

Aeopile

New member
Local time
Today, 18:37
Joined
Nov 30, 2010
Messages
3
I have been struggling with this, and doevents does not work for me. Has the original author tried removing those bits and see if the code succeeds?

This is extremely simple code that I am testing with in order to test killing excel when all done with the spreadsheet.... any suggestions on what I'm doing wrong?

Code:
Function Test()
    
Dim objXL As Object
Dim xlWB As Object
Set objXL = CreateObject("Excel.Application")
Set xlWB = objXL.Workbooks.Open("C:\Temp\Book1.xlsx")

MsgBox objXL.Sheets("Sheet1").Range("A1").value

xlWB.Close False
objXL.Quit

End Function
 

boblarson

Smeghead
Local time
Today, 15:37
Joined
Jan 12, 2001
Messages
32,059
any suggestions on what I'm doing wrong?

Code:
Function Test()
 
Dim objXL As Object
Dim xlWB As Object
Set objXL = CreateObject("Excel.Application")
Set xlWB = objXL.Workbooks.Open("C:\Temp\Book1.xlsx")
 
MsgBox objXL.Sheets("Sheet1").Range("A1").value
 
xlWB.Close False
objXL.Quit
 
End Function
I just tried your code and it works fine for me. Have you made sure you haven't gotten any extra Excel.EXE's running in your task manager BEFORE testing?
 

omnialive

Registered User.
Local time
Today, 15:37
Joined
Sep 17, 2010
Messages
23
I have been struggling with this, and doevents does not work for me. Has the original author tried removing those bits and see if the code succeeds?

This is extremely simple code that I am testing with in order to test killing excel when all done with the spreadsheet.... any suggestions on what I'm doing wrong?

Code:
Function Test()
    
Dim objXL As Object
Dim xlWB As Object
Set objXL = CreateObject("Excel.Application")
Set xlWB = objXL.Workbooks.Open("C:\Temp\Book1.xlsx")

MsgBox objXL.Sheets("Sheet1").Range("A1").value

xlWB.Close False
objXL.Quit

End Function

First thing it looks like you are NOT closing the work book, unless I am not understanding what the "xlWB.Close False" line of code is doing. Wouldn't you want that to be "True"? As you can tell, there are stark differences between how you instantiate the object variables and how I did it which I'm sure will change the technique you have to use as well.

I don't see in your example code where you are setting the object variables to "Nothing" as I did in my code above. Give that a whirl. Beside that, it could be variances between the versions of Access and Excel you are using vs the versions I am using (2007) as well as the version of Windows (Vista Enterprise SP 2).
 

boblarson

Smeghead
Local time
Today, 15:37
Joined
Jan 12, 2001
Messages
32,059
First thing it looks like you are NOT closing the work book, unless I am not understanding what the "xlWB.Close False" line of code is doing.
Yes, you are misunderstanding that. The False part means that it isn't SAVING. It is closing but not saving.

Wouldn't you want that to be "True"?
Not unless they wanted to save changes.

I don't see in your example code where you are setting the object variables to "Nothing" as I did in my code above.
Yes, you can use

Set objXL = Nothing

and that might help but you don't need to do it with the workbook object because it is destroyed along with the application object.


Beside that, it could be variances between the versions of Access and Excel you are using vs the versions I am using (2007) as well as the version of Windows (Vista Enterprise SP 2).
Well, this code EXACTLY as written works fine for me on Access 2003 XP SP3, Access 2007 XP SP3, Access 2007 Win7, Access 2010 Win7. I seriously doubt it works differently on Vista. It runs fine and there is no leftover instance of Excel running.
 

Aeopile

New member
Local time
Today, 18:37
Joined
Nov 30, 2010
Messages
3
Well, this code EXACTLY as written works fine for me on Access 2003 XP SP3, Access 2007 XP SP3, Access 2007 Win7, Access 2010 Win7. I seriously doubt it works differently on Vista. It runs fine and there is no leftover instance of Excel running.

Assuming you are referring to the code I posted, that is quite discouraging, because I'm overrun with Zombie Excels! BTW, I have tried with "Set nothing" stuff, and it did not help, I just failed to include that line on this particular test. As I understand it though, the Quit is what should shut down Excel, the "set nothing" is just memory reallocation for Access.

I think I'll write a function to check for processes stamped with EXCEL. Tell the user to shut down EXCEL. Afterwards, do my application stuff, afterwhich I kill with extreme prejudice any lingering EXCEL's afterwards.

*sigh* - oh well at least I'll learn more about interacting with processes.
 

boblarson

Smeghead
Local time
Today, 15:37
Joined
Jan 12, 2001
Messages
32,059
Are you including ALL of the code you are using for this? I suspect not. If you have ANY CODE WHATSOEVER which isn't tied to an instantiated object it will open an extra instance of Excel.
 

Aeopile

New member
Local time
Today, 18:37
Joined
Nov 30, 2010
Messages
3
Are you including ALL of the code you are using for this? I suspect not. If you have ANY CODE WHATSOEVER which isn't tied to an instantiated object it will open an extra instance of Excel.

Yes, that is ALL the code, just that function alone leaves EXCEL zombie on my work computer. I have not tried it on a different machine though, but that may be worth a shot.

*Update*

I just tested the above code on a virtual machine with Access 2007 and Excel 2007 and I have success! So, there is something on my primary work computer that stops this from working. I'm going to disable Trend Micro to see if that helps.
 
Last edited:

elliotgr

Registered User.
Local time
Tomorrow, 01:37
Joined
Dec 30, 2010
Messages
67
I had a similar problem. It was caused by making the Excel application visible during testing so the effects could be checked live. The Excel workbook saved and closed but the Excel app remained open. The only was around this was not to make the workbook visible.

Code used to make work book visible

Set MyXL = GetObject("C:\Test.xlsx")
MyXL.Application.Visible = True
MyXL.Parent.Windows(1).Visible = True
MyXL.Worksheets(1).Activate
MyXL.Worksheets(1).Name = "TEST"
MyXL.Windows("Test.xlsx").Visible = True
MyXL.Save
MyXL.Close
MyXL.Quit
Set MyXL = Nothing
 

boblarson

Smeghead
Local time
Today, 15:37
Joined
Jan 12, 2001
Messages
32,059
I had a similar problem. It was caused by making the Excel application visible during testing so the effects could be checked live. The Excel workbook saved and closed but the Excel app remained open. The only was around this was not to make the workbook visible.

Code used to make work book visible

Set MyXL = GetObject("C:\Test.xlsx")
MyXL.Application.Visible = True
MyXL.Parent.Windows(1).Visible = True
MyXL.Worksheets(1).Activate
MyXL.Worksheets(1).Name = "TEST"
MyXL.Windows("Test.xlsx").Visible = True
MyXL.Save
MyXL.Close
MyXL.Quit
Set MyXL = Nothing
Your code for visibility is way too much and probably why you were having issues. All you would need is this:
Code:
Dim strFile As String
 
strFile = "C:\Test.xlsx"
 
Set MyXL = CreateObject("Excel.Application")
With MyXL
   .Visible = True
   .WorkBooks.Open(strFile)
   .Worksheets(1).Activate
   .Worksheets(1).Name = "TEST"
   .ActiveWorkbook.Close True  ' which saves on close because of TRUE
   .Quit
Set MyXL = Nothing
And that would avoid your problem - not the visible part but using the correct code to keep things working properly. There's no reason to use the Parent.Windows, and .Windows code.
 

elliotgr

Registered User.
Local time
Tomorrow, 01:37
Joined
Dec 30, 2010
Messages
67
Thanks.
I had to force the second visible because when the workbook saved, it made it hidden. Not sure why, and the only way was to make it visible twice.
However I will try your code and see if it also remove the hide problem.
 

carlssc

New member
Local time
Today, 18:37
Joined
May 14, 2020
Messages
1
does anyone have a solution for closing Excel itself from Access 2007 WITHOUT KNOWING THE FILE NAMES. i need to have Excel itself closed prior to creating a worksheet in my Access app. all the code i reviewed above seemed to know the name of the file. i have code for that scenario:

Function Close_WB_OPEN(ByVal sBookName)
Set MyXL = GetObject(sBookName)
MyXL.Close False
Set MyXL = Nothing
End Function
(yes, i am asking an Access in 2007 question in the year 2020. dont tell me to upgrade...)
 

Isaac

Lifelong Learner
Local time
Today, 15:37
Joined
Mar 14, 2017
Messages
8,777
There is a lot of code out there for killing a task manager process. Here is one I came across. Be sure the end user is OK with this ...

 

Users who are viewing this thread

Top Bottom