0

I'm having problems with an application I programmed in VB.NET environment that has to open an Excel file to fetch some data and then close it. The problem is that no matter what I do, in Windows' task manager the application is always there.

What the application does:

 EA = New Excel.Application
 OptionsSource = EA.Workbooks.Open(Filename:=myFileName, ReadOnly:=True)
 wks = OptionsSource.Worksheets(1)
 ' reads data from cells and stores it in an array
 wks = Nothing
 OptionsSource.Close()
 OptionsSource = Nothing
 EA.Quit()
 EA = Nothing

What I tried:

 GC.Collect() ' after the .Close(), but really I tried it everywhere
 Marshal.ReleaseComObject(Obj) ' after each Object

What I've seen do:

 Application.Exit() ' seems like I cannot use it somehow

What I could do (but I haven't yet figured out how):

 Process.Kill ' I'd have to find exactly the process that is started by EA = New Excel.Application and then kill it

What I cannot definitely do is to kill all the processes that are called "EXCEL" (I know how to do this, though).

How do I nuke my Excel.Application once and for all?

Noldor130884
  • 974
  • 2
  • 16
  • 40
  • It's hard to provide a specific suggestion without seeing every bit if your code that references excel in any way, but in general you need to use/keep/dispose explicit references to everything that touches excel. I.E. don't use stuff like Optionsource.Worksheets(1). Behind the scenes, that's creating a reference to a "Sheets" variable that will hang. Explicitly create a sheets variable = OptionsSource.Sheets, and then wks = sheets(1)...etc and then dispose each of them later. Look out for stuff like that in the rest of your code – soohoonigan Jan 22 '18 at 15:52
  • 1
    Thanks @AndrewMorton. Using a function to do my Excel stuff and then adding two times `GC.Collect()` and `GC.WaitForPendingFinalizers()` outside the function worked perfectly. – Noldor130884 Jan 23 '18 at 08:58

2 Answers2

2

This is a cautious (perhaps overly cautious) method which always works for me. I reference each COM object so I can release them later. I avoid having more than one . when accessing a COM object i.e. instead of getting xlSheet from xlApp.Workbooks.Open(Filename:= FileName, ReadOnly:= true).Sheets(1), I only go one level each time, so I need the variables xlApp, xlBooks, xlBook, and xlSheet. Some people will say this is unnecessary, but it doesn't bother me because I have had success with this method.

It's similar to how you do it anyway. But you may want to at least try EA.Quit() if nothing else.

Dim xlApp As Microsoft.Office.Interop.Excel.Application = Nothing
Dim xlBooks As Microsoft.Office.Interop.Excel.Workbooks = Nothing
Dim xlBook As Microsoft.Office.Interop.Excel.Workbook = Nothing
Dim xlSheet As Microsoft.Office.Interop.Excel.Worksheet = Nothing
Dim xlRange As Microsoft.Office.Interop.Excel.Range = Nothing

Try
    xlApp = New Microsoft.Office.Interop.Excel.Application()
    xlBooks = xlApp.Workbooks
    xlBook = xlBooks.Open(Filename:= FileName, ReadOnly:= true)
    xlSheet = xlBook.Sheets(1)
    ' do other stuff
Finally
    GC.Collect()
    GC.WaitForPendingFinalizers()
    If xlRange IsNot Nothing Then Marshal.ReleaseComObject(xlRange)
    If xlSheet IsNot Nothing Then Marshal.ReleaseComObject(xlSheet)
    If xlBook IsNot Nothing Then 
        xlBook.Close(SaveChanges:= false)
        Marshal.ReleaseComObject(xlBook)
    End If
    If xlBooks IsNot Nothing Then
        xlBooks.Close()
        Marshal.ReleaseComObject(xlBooks)
    End If
    If xlApp IsNot Nothing
        xlApp.Quit()
        Marshal.ReleaseComObject(xlApp)
    End If
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Try
djv
  • 15,168
  • 7
  • 48
  • 72
  • First of all thanks a lot for the suggestion. I am not sure whether to upvote or not because even if it works, this basically makes my code **very** long. According to @Andrew Morton and the answer he linked, we apparently don't even need the `Marshal.ReleaseComObject` anyway. Maybe it's me being a programmer noob, but I still have to understand exactly how they work... – Noldor130884 Jan 23 '18 at 08:57
  • Let's see why this is long. First, the objects each have their own reference so they can be release them. Second, you should have a `Try...Finally` to ensure that the cleanup happens always, even if there is an exception - add `Catch` if you like. Third, the explicit cleanup ensures that the COM objects are released at this moment. While you don't need Marshal.ReleaseComObject and you can allow the GC to eventually clean up, this forces it to happen. If your application exits right after this function, this version explicitly cleans up, while the GC may never have a chance to clean up ... – djv Jan 23 '18 at 16:12
  • ... Overly cautious? Maybe, but this way you take control of the clean up instead of waiting for the GC to get around it. – djv Jan 23 '18 at 16:13
0

Kill the process by its Hwnd. Not that beautiful but it seems to work:

Waescher
  • 5,361
  • 3
  • 34
  • 51