0

I'm currently trying to make a macro that opens a user defined excel spreadsheet, extracts some data for use in the word document and then closes it. My problem is that when I run the macro, the spreadsheet that I opened is still technically open as a background process in my task manager. I read on another stack overflow question that the reason is because visual basic will not release the reference object from excel until I close out of Microsoft Word. However, even after closing out of Word, the excel background process is still going and I can only stop it by ending the task in the task manager. To clarify, if I run the macro, close Word and then try to open the excel file, I can get in without telling me it's a read only file. However, if I don't close out of Word and I try to go into the spreadsheet after running the macro, then it tells me that it's a read only file. Below is the code I'm using that is causing this problem for me. Thanks to anyone who can help.

Sub UpdateProposal()

'Declares variables
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim SpreadsheetPath As String
Dim xlSheet As Excel.Worksheet
Dim xlRange As Excel.Range
Dim ExcelWasNotRunning As Boolean
Dim ProposalInfoArr(1 To 30) As String

'Skips to ErrorHandler if user cancels out of file dialog
On Error GoTo ErrorHandler

'Display a Dialog Box that allows to select a single file.
'The path for the file picked will be stored in SpreadsheetPath variable
With Application.FileDialog(msoFileDialogFilePicker)

    'Makes sure the user can select only one file
    .AllowMultiSelect = False

    'Filter to just the following types of files to narrow down selection options
    .Filters.Add "Excel Files", "*.xlsx; *.xlsm; *.xls; *.xlsb", 1

    'Show the dialog box
    .Show

    'Stores in SpreadsheetPath variable
    SpreadsheetPath = .SelectedItems.Item(1)

End With

'If Excel is running, get a handle on it; otherwise start a new instance of Excel
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")

If Err Then
   ExcelWasNotRunning = True
   Set xlApp = New Excel.Application
End If

'If you want Excel to be visible, you could add the line: xlApp.Visible = True here; but your code will run faster if you don't make it visible

'Open the workbook
Set xlBook = xlApp.Workbooks.Open(FileName:=SpreadsheetPath)

'''Extracts Data

'Quits out of Excel if it was not running previous to running the macro.
If ExcelWasNotRunning Then
    xlApp.DisplayAlerts = False
    xlApp.Quit
End If


'Make sure you release object references.
Set xlRange = Nothing
Set xlSheet = Nothing
Set xlBook = Nothing
Set xlApp = Nothing

'Ends the macro before the error handler
Exit Sub

'Ends Macro
ErrorHandler:
    MsgBox "The following error occurred: " & Err.Description

End Sub
btorres
  • 3
  • 3
  • So, after a little investigation, I cannot repo your issue witht the code posted. I ran the code, it creates an Excel instance, opens a workbook (Excel apears in TM), Quits Excel (Excel is removed from TM). IIRC the `Set ... = Nothing` lines are also not required at the end of the Sub, as this happens behind the scenes. My conclusion is that something in the "'Extracts Data" code not posted is holding the instance open – chris neilsen Mar 05 '20 at 22:05

1 Answers1

2

You are defining the objects correctly:

Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Dim xlRange As Excel.Range

but you forgot about the implicitly used Workbooks object... as most of the answers you will find do... which means it doesn't get released. So do it like this:

Dim SpreadsheetPath As String
Dim xlApp As Excel.Application
Dim xlBooks As Excel.Workbooks
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Dim xlRange As Excel.Range

SpreadsheetPath = "C:\MyPath\MyFile.xlsx"
Set xlApp = New Excel.Application
' Set xlApp = GetObject(, "Excel.Application") ' or attach to an existing one
Set xlBooks = xlApp.Workbooks
Set xlBook = xlBooks.Open(FileName:=SpreadsheetPath) ' you can use .Add instead if someone else may have it open already
Set xlSheet = xlBook.Worksheets(1)
Set xlRange = xlSheet.Range("A1")

' do stuff with the worksheet/range
xlRange.Value = "foo"

' the order matters 
' just like it does 
' when you create the objects
Set xlRange = Nothing
Set xlSheet = Nothing
xlBook.Close False
Set xlBook = Nothing
Set xlBooks = Nothing
xlApp.Quit
Set xlApp = Nothing

However, you may find that it still isn't getting released when you want, but it will get released when you close the program you are using to create it (in your case, MS-Word) as that is (presumably) when Windows does its built-in garbage collection.

Note: I removed the error handling just to keep it a clean example, but you can leave that in

braX
  • 11,506
  • 5
  • 20
  • 33
  • did you try the OPs code as posted? For me it's no-repo. – chris neilsen Mar 05 '20 at 22:08
  • No, but i've run into this issue many times in the past, and this is the best option I know. Granted, it was in an older version of Office and older version of Windows, so maybe things are different now with more recent versions. – braX Mar 05 '20 at 22:10
  • What you posted here is basically the 2 dot rule, which has been debunked. (There is a good post on this by Hanns Passant, I'll try and find it). It would be nice to get some feedback from the OP on if this solves their issue, but I really suspect there is something in the unposted code that is the root cause. – chris neilsen Mar 05 '20 at 22:15
  • I would be interested in seeing this other post myself if it works better than the method I use. – braX Mar 05 '20 at 22:17
  • 1
    [Here it is](https://stackoverflow.com/a/25135685/445425) Note it's only tangentailly related as it's about .Net, not VBA. But it's still COM involved here, so at least interesting – chris neilsen Mar 05 '20 at 22:21
  • @chrisneilsen There is another method I've used that involves building a collection of existing instances of Excel beforehand, running your code, and then once it is completed, kill off all the Excel processes that are not the ones in the collection. That has worked well too, but it just seems like a sloppy alternative. – braX Mar 05 '20 at 22:24
  • Thank you braX. Your solution solved my problem. There still is excel running as a background process in my task manager but it is not resulting in a read only status of the file I'm trying to access which solves my problem. @chris neilsen, The problem still persisted even if I commented out all the bits in the middle and only had a MsgBox of one of the values in the spreadsheet. Thank you for commenting anyways. – btorres Mar 06 '20 at 13:32
  • 1
    UPDATE. The Background process went away after I also erased the values in all of the variables I was using to hold data from the spreadsheet. Thank you both for the help. – btorres Mar 06 '20 at 13:40
  • @chrisneilsen - Thanks for sharing the link - It did give me a bit more insight into why it would sometimes work and sometimes not work. – braX Mar 06 '20 at 13:40