Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VB.NET Excel Program Leaves EXCEL.EXE floating after completion

Tags:

.net

excel

vb.net

I am writing a program that loops through all of the sheets in an Excel workbook and saves each sheet as its own workbook. It turned out to be a bit trickier than I expected, since the Sheet.Copy method creates a strange object (see here for the MSDN discussion that I believe is relevant: http://msdn.microsoft.com/en-us/library/ms178779.aspx).

Anyway, I found another Stack Overflow post that got me to where I am, which is essentially complete, outside of one hanging EXCEL.EXE process that is left after the program completes (check the update for one more issue that came up, but I think they are related).

Here is my code:

Imports System.Data
Imports System.IO
Imports Microsoft.Office.Interop
Imports Office = Microsoft.Office.Core
Imports xlNS = Microsoft.Office.Interop.Excel
Imports System.Runtime.InteropServices

Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    'Get information from text boxes
    Dim InputLocation As String
    Dim OutputLocation As String

    InputLocation = InputLoc.Text & "\" & FileName.Text

    If OutputLoc.Text = "" Then
        OutputLocation = InputLoc.Text
    Else
        OutputLocation = OutputLoc.Text
    End If

    'Make file to save files in
    ' Get date and time in filename as well
    Dim TLDateTime As String
    Dim TLDay As String
    Dim TLMonth As Integer
    Dim TLYear As Integer
    Dim TLHour As Integer
    Dim TLMinute As Integer
    Dim TLDate As String
    Dim TLTime As String
    Dim TLSecond As Integer

    TLDay = DateTime.Now.Day
    TLMonth = DateTime.Now.Month
    TLYear = DateTime.Now.Year
    TLHour = DateTime.Now.Hour
    TLMinute = DateTime.Now.Minute
    TLSecond = DateTime.Now.Second

    Dim MyDate As New DateTime(TLYear, TLMonth, TLDay, TLHour, TLMinute, TLSecond)
    Dim MyString As String = MyDate.ToString("MMMddyyyy_HHmmss")
    TLDate = TLMonth.ToString + TLDay.ToString + TLYear.ToString
    TLTime = TLHour.ToString + TLMinute.ToString
    TLDateTime = TLDate + "_" + TLTime

    Try
        Directory.CreateDirectory(OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime)
        OutputLocation = OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime
    Catch
        MsgBox("Trying to create a file that exists, please delete it. If the file does not exist check to make sure your output location exists")
    End Try

    'Open up excel file with information in it

    Dim xlApp1 As Excel.Application
    Dim locs As Excel.Workbook

    Dim exportsheet As Excel.Worksheet
    xlApp1 = New Excel.Application
    xlApp1.Visible = True
    xlApp1.Application.DisplayAlerts = False
    locs = xlApp1.Workbooks.Open(InputLocation)

    'locsws = locs.ActiveSheet
    Dim wkshtcount = locs.Worksheets.Count - 1
    Dim fileNames As New ArrayList

    For counter = 1 To wkshtcount + 1
        'identify and copy sheet to move
        exportsheet = CType(locs.Worksheets(counter), Excel.Worksheet)
        fileNames.Add(exportsheet.Name)
        exportsheet.Copy(Type.Missing, Type.Missing)

        exportsheet = xlApp1.Workbooks("Book" & counter).Sheets(1)

        exportsheet.SaveAs(Filename:=OutputLocation & "\" & fileNames(counter - 1) & ".xlsx")

        'close excel and release com objects
        System.Runtime.InteropServices.Marshal.ReleaseComObject(exportsheet)
        exportsheet = Nothing
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1.ActiveWorkbook)
        xlApp1.ActiveWorkbook.Close(False)

    Next
    'close excel and release com objects
    locs.Close(False)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(locs)
    locs = Nothing
    xlApp1.Quit()
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1)
    xlApp1 = Nothing


End Sub
End Class

Now I think the problem comes from the end of the loop, where I try to close the export file and the new worksheet it creates:

        'close excel and release com objects
        System.Runtime.InteropServices.Marshal.ReleaseComObject(exportsheet)
        exportsheet = Nothing
        xlApp1.Workbooks(fileNames(counter - 1)).Close(False)

I can't figure out what to do to release the ComObject for the new worksheet that is created. I have been trying all sorts of things, but it always throws a COM error when I do it and if I try to define it as nothing (like I do with exportsheet) is says that it is read only by default, so I can't do it. It seems like it should be something as simple as:

System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1.Workbooks(fileNames(counter - 1)))

But that is not the case. I have tried a few variants of this and I am thinking it has to do with the MSDN link above, but I can't quite sort out what to do. So the code works for my purposes, outside of leaving one EXCEL.EXE after it is done.

As far as a test file, I am just using an Excel file with 3 sheets and I put some information on each and change the sheet name, so it is easy to see if it is working or not.

Any thoughts would be appreciated, thanks.

Update: I just turned the visibility off from my main Excel application and things still pop up, which leads me to believe that the way I am using Copy is creating a new Excel application, but I am not really sure how to reference it. If anyone knows how to turn off visibility there as well, it would be much appreciated.

Final Update: On the off chance that some poor soul ran into the same issue I was running into, the first update should solve it, but it is also important to note that the excel.exe will hang until you close the application. I am report automation code as a windows form application (so coworkers can give the file location and such) and there is going to be an excel.exe process running until you close the pop up window from the program. Maybe garbage collect does not run until you close the application window or it just hangs on to an instance of excel.exe for some other reason.

like image 250
asjohnson Avatar asked Feb 24 '12 18:02

asjohnson


2 Answers

Below is code that works for me (NOTE order that I release objects, which is important)

xlWorkBook.Close()
xlApp.Quit()
ReleaseObject(xlWorkSheet)
ReleaseObject(xlWorkBook)
ReleaseObject(xlApp)

Private Sub ReleaseObject(ByVal obj As Object)
  Try
    System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
    obj = Nothing
  Catch ex As Exception
    obj = Nothing
  Finally
    GC.Collect()
  End Try
End Sub
like image 63
Darren Avatar answered Mar 15 '23 00:03

Darren


You might need to do the following if you have unreferenced COM objects that are being created:

GC.Collect()
GC.WaitForPendingFinalizers()

Taken from this MSDN forum post: http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/cb5f7948-c229-483e-846b-a1cfbbcd86ca/

like image 20
Jared Shaver Avatar answered Mar 14 '23 22:03

Jared Shaver