17

I have a small problem that I can't seem to figure out. I am saving a DataGridView (it's contents) to an xls file. I have no problem in doing so except in my task manager its still showing up that it's running. I have called:

  xlApp.Application.Quit() 

This is declared as:

  Dim xlApp As New excel.Application

This seems to not work, BUT this is the same way I quit when I let the user choose to export it to a Word Document. Im not sure where I am going wrong...

Here is my complete code

Imports Word = Microsoft.Office.Interop.Word
 Imports Excel = Microsoft.Office.Interop.Excel

 Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    For x As Integer = 1 To 3500
        DataGridView1.Rows.Add(New Object() {"r" & x.ToString & "c1", "r" & x.ToString & "c2", "r" & x.ToString & "c3", "r" & x.ToString & "c4", "r" & x.ToString & "c5"})
    Next
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    exportToWord (DataGridView1)
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim xlApp As New Excel.Application
    Dim xlWorkBook As Excel.Workbook
    Dim xlWorkSheet As Excel.Worksheet
    'Dim misValue As Object = System.Reflection.Missing.Value


    xlWorkBook = xlApp.Workbooks.Add
    xlWorkSheet = DirectCast(xlWorkBook.Sheets("sheet1"), Excel.Worksheet)

    xlApp.Visible = True

    Dim headers = (From ch In DataGridView1.Columns _
                  Let header = DirectCast(DirectCast(ch, DataGridViewColumn).HeaderCell, DataGridViewColumnHeaderCell) _
                  Select header.Value).ToArray()
    Dim headerText() As String = Array.ConvertAll(headers, Function(v) v.ToString)

    Dim items() = (From r In DataGridView1.Rows _
          Let row = DirectCast(r, DataGridViewRow) _
          Where Not row.IsNewRow _
          Select (From cell In row.Cells _
              Let c = DirectCast(cell, DataGridViewCell) _
              Select c.Value).ToArray()).ToArray()

    Dim table As String = String.Join(vbTab, headerText) & Environment.NewLine
    For Each a In items
        Dim t() As String = Array.ConvertAll(a, Function(v) v.ToString)
        table &= String.Join(vbTab, t) & Environment.NewLine
    Next
    table = table.TrimEnd(CChar(Environment.NewLine))
    Clipboard.SetText (table)

    Dim alphabet() As Char = "abcdefghijklmnopqrstuvwxyz".ToUpper.ToCharArray

    Dim range As excel.Range = xlWorkSheet.Range("B2:" & alphabet(headerText.Length) & (items.Length + 2).ToString)

    range.Select()
    xlWorkSheet.Paste()

    range.Borders(Excel.XlBordersIndex.xlDiagonalDown).LineStyle = Excel.XlLineStyle.xlLineStyleNone
    range.Borders(Excel.XlBordersIndex.xlDiagonalUp).LineStyle = Excel.XlLineStyle.xlLineStyleNone
    With range.Borders(Excel.XlBordersIndex.xlEdgeLeft)
        .LineStyle = Excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = Excel.XlBorderWeight.xlMedium
    End With
    With range.Borders(Excel.XlBordersIndex.xlEdgeTop)
        .LineStyle = Excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = Excel.XlBorderWeight.xlMedium
    End With
    With range.Borders(Excel.XlBordersIndex.xlEdgeBottom)
        .LineStyle = Excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = Excel.XlBorderWeight.xlMedium
    End With
    With range.Borders(Excel.XlBordersIndex.xlEdgeRight)
        .LineStyle = Excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = Excel.XlBorderWeight.xlMedium
    End With
    With range.Borders(Excel.XlBordersIndex.xlInsideVertical)
        .LineStyle = Excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = Excel.XlBorderWeight.xlThin
    End With
    With range.Borders(Excel.XlBordersIndex.xlInsideHorizontal)
        .LineStyle = Excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = Excel.XlBorderWeight.xlThin
    End With

    'xlApp.Visible = True

    xlWorkBook.SaveAs("C:\Users\CoDeXeR\Desktop\Word1.xls", True)
    xlWorkBook.Close()
    xlApp.Application.Quit()

    ReleaseObject(xlWorkSheet) '<~~~ Added as per comment from deleted post
    ReleaseObject (xlWorkBook)
    ReleaseObject (xlApp)


End Sub

Public Sub exportToWord(ByVal dgv As DataGridView)
    ' Create Word Application
    Dim oWord As Word.Application = DirectCast(CreateObject("Word.Application"), Word.Application)

    ' Create new word document
    Dim oDoc As Word.Document = oWord.Documents.Add()


    Dim headers = (From ch In dgv.Columns _
                  Let header = DirectCast(DirectCast(ch, DataGridViewColumn).HeaderCell, DataGridViewColumnHeaderCell) _
                  Select header.Value).ToArray()
    Dim headerText() As String = Array.ConvertAll(headers, Function(v) v.ToString)

    Dim items() = (From r In dgv.Rows _
          Let row = DirectCast(r, DataGridViewRow) _
          Where Not row.IsNewRow _
          Select (From cell In row.Cells _
              Let c = DirectCast(cell, DataGridViewCell) _
              Select c.Value).ToArray()).ToArray()

    Dim table As String = String.Join(vbTab, headerText) & Environment.NewLine
    For Each a In items
        Dim t() As String = Array.ConvertAll(a, Function(v) v.ToString)
        table &= String.Join(vbTab, t) & Environment.NewLine
    Next
    table = table.TrimEnd(CChar(Environment.NewLine))
    Clipboard.SetText (table)

    Dim oTable As Word.Table = oDoc.Tables.Add(oDoc.Bookmarks.Item("\endofdoc").Range, items.Count + 1, headers.Count)

    oTable.Range.Paste()

    'make the first row bold, fs 14 + change textcolor
    oTable.Rows.Item(1).range.Font.Bold = &H98967E
    oTable.Rows.Item(1).range.Font.Size = 14
    oTable.Rows.Item(1).range.Font.Color = Word.WdColor.wdColorWhite

    'change backcolor of first row
    oTable.Rows.Item(1).range.Shading.Texture = Word.WdTextureIndex.wdTextureNone
    oTable.Rows.Item(1).range.Shading.ForegroundPatternColor = Word.WdColor.wdColorAutomatic
    oTable.Rows.Item(1).range.Shading.BackgroundPatternColor = Word.WdColor.wdColorLightBlue

    ''set table borders
    'With oTable.Range.Tables(1)
    '    With .Borders(Word.WdBorderType.wdBorderLeft)
    '        .LineStyle = Word.WdLineStyle.wdLineStyleSingle
    '        .LineWidth = Word.WdLineWidth.wdLineWidth100pt
    '        .Color = Word.WdColor.wdColorAutomatic
    '    End With
    '    With .Borders(Word.WdBorderType.wdBorderRight)
    '        .LineStyle = Word.WdLineStyle.wdLineStyleSingle
    '        .LineWidth = Word.WdLineWidth.wdLineWidth100pt
    '        .Color = Word.WdColor.wdColorAutomatic
    '    End With
    '    With .Borders(Word.WdBorderType.wdBorderTop)
    '        .LineStyle = Word.WdLineStyle.wdLineStyleSingle
    '        .LineWidth = Word.WdLineWidth.wdLineWidth100pt
    '        .Color = Word.WdColor.wdColorAutomatic
    '    End With
    '    With .Borders(Word.WdBorderType.wdBorderBottom)
    '        .LineStyle = Word.WdLineStyle.wdLineStyleSingle
    '        .LineWidth = Word.WdLineWidth.wdLineWidth100pt
    '        .Color = Word.WdColor.wdColorAutomatic
    '    End With
    '    With .Borders(Word.WdBorderType.wdBorderHorizontal)
    '        .LineStyle = Word.WdLineStyle.wdLineStyleSingle
    '        .LineWidth = Word.WdLineWidth.wdLineWidth050pt
    '        .Color = Word.WdColor.wdColorAutomatic
    '    End With
    '    With .Borders(Word.WdBorderType.wdBorderVertical)
    '        .LineStyle = Word.WdLineStyle.wdLineStyleSingle
    '        .LineWidth = Word.WdLineWidth.wdLineWidth050pt
    '        .Color = Word.WdColor.wdColorAutomatic
    '    End With
    '    .Borders(Word.WdBorderType.wdBorderDiagonalDown).LineStyle = Word.WdLineStyle.wdLineStyleNone
    '    .Borders(Word.WdBorderType.wdBorderDiagonalUp).LineStyle = Word.WdLineStyle.wdLineStyleNone
    '    .Borders.Shadow = False
    'End With
    ' Save this word document
    oDoc.SaveAs("C:\Users\CoDeXeR\Desktop\Word1.doc", True)
    oDoc.Close()
    oWord.Application.Quit()
    'oWord.Visible = True

End Sub

Public Sub exportToExcel(ByVal dgv As DataGridView)

End Sub

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

 End Class
peterh
  • 11,875
  • 18
  • 85
  • 108
Trevor
  • 7,777
  • 6
  • 31
  • 50
  • can you zip your project and upload it to wikisend.com and then share the link here. Let me test it for you... – Siddharth Rout Mar 29 '13 at 07:25
  • Also I hope you tested the change I suggested after ensuring that there was no excel instance in the task manager? – Siddharth Rout Mar 29 '13 at 07:29
  • Are you talking about the xlWorksheet being released? If so, of course and it didn't work... Anyways here's the .zip http://wikisend.com/download/143434/export dgv to word ~ excel (1).zip – Trevor Mar 29 '13 at 07:30
  • Can you try releasing the `range` variable as well? I think its a COM object and could potentially be holding things up. – shahkalpesh Mar 29 '13 at 07:40
  • @shahkalpesh I just tried that and no luck... Thanks though. – Trevor Mar 29 '13 at 07:44
  • A few suggestions. Try `xlApp.Quit` instead of `xlApp.Application.Quit`. Also, make sure the order of releasing objects is proper (range, worksheet, workbook, app). Try pulling out the code, i.e work with excel, see it gets released followed by adding workbook related code, followed by adding worksheet related code and so on. – shahkalpesh Mar 29 '13 at 08:22
  • I have found the actual cause. I am looking for a proper way to explain it with example... – Siddharth Rout Mar 29 '13 at 08:37
  • Awesome, I'm sure it's something that slipped right by me... – Trevor Mar 29 '13 at 08:43
  • Posting a followup in my post below – Siddharth Rout Mar 29 '13 at 09:05
  • @SiddharthRout: Was it `GC.Collect`? If that is removed, does the excel instance hang in the memory? I thought that it could be `range` object not being released as well, as mentioned in my comment above. Thanks. – shahkalpesh Mar 30 '13 at 15:13

11 Answers11

35

Just Calling .Quit() will not remove the Application from memory. It is very important to close the objects after you are done with your coding. This ensures that all objects are released properly and nothing remains in the memory.

See this example

Imports Excel = Microsoft.Office.Interop.Excel
 
Public Class Form1
    '~~> Define your Excel Objects
    Dim xlApp As New Excel.Application
    Dim xlWorkBook As Excel.Workbook
 
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        '~~> Add a New Workbook
        xlWorkBook = xlApp.Workbooks.Add
 
        '~~> Display Excel
        xlApp.Visible = True
 
        '~~> Do some stuff Here
 
        '~~> Save the file
        xlWorkBook.SaveAs(Filename:="C:\Tutorial\SampleNew.xlsx", FileFormat:=51)
 
        '~~> Close the File
        xlWorkBook.Close()
 
        '~~> Quit the Excel Application
        xlApp.Quit()
 
        '~~> Clean Up
        releaseObject (xlApp)
        releaseObject (xlWorkBook)
    End Sub
 
    '~~> Release the objects
    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
 
    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Me.Close()
    End Sub
End Class

Also worth mentioning is the 2 DOT Rule.

If you love automating Excel from VB.Net then you might also want to have a look at this link.

FOLLOWUP

The problem is the 2 DOT Rule as I mentioned above. When you use the 2 DOT Rule (Ex: Excel.XlBordersIndex.xlDiagonalDown) then you have to do the Garbage Collection by using GC.Collect(). So All you need to do is add this part

    Finally
        GC.Collect()

in the Private Sub ReleaseObject(ByVal obj As Object)

Private Sub ReleaseObject(ByVal obj As Object)
    Try
        Dim intRel As Integer = 0
        Do
            intRel = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
        Loop While intRel > 0
        MsgBox("Final Released obj # " & intRel)
    Catch ex As Exception
        MsgBox("Error releasing object" & ex.ToString)
        obj = Nothing
    Finally
        GC.Collect()
    End Try
End Sub

FINAL CODE

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim xlApp As New excel.Application
    Dim xlWorkBook As excel.Workbook
    Dim xlWorkSheet As excel.Worksheet
    Dim xlRange As excel.Range
    'Dim misValue As Object = System.Reflection.Missing.Value

    xlWorkBook = xlApp.Workbooks.Add
    xlWorkSheet = DirectCast(xlWorkBook.Sheets("sheet1"), excel.Worksheet)

    xlApp.Visible = True

    Dim headers = (From ch In DataGridView1.Columns _
                  Let header = DirectCast(DirectCast(ch, DataGridViewColumn).HeaderCell, DataGridViewColumnHeaderCell) _
                  Select header.Value).ToArray()
    Dim headerText() As String = Array.ConvertAll(headers, Function(v) v.ToString)

    Dim items() = (From r In DataGridView1.Rows _
          Let row = DirectCast(r, DataGridViewRow) _
          Where Not row.IsNewRow _
          Select (From cell In row.Cells _
              Let c = DirectCast(cell, DataGridViewCell) _
              Select c.Value).ToArray()).ToArray()

    Dim table As String = String.Join(vbTab, headerText) & Environment.NewLine
    For Each a In items
        Dim t() As String = Array.ConvertAll(a, Function(v) v.ToString)
        table &= String.Join(vbTab, t) & Environment.NewLine
    Next
    table = table.TrimEnd(CChar(Environment.NewLine))
    Clipboard.SetText(table)

    Dim alphabet() As Char = "abcdefghijklmnopqrstuvwxyz".ToUpper.ToCharArray

    xlRange = xlWorkSheet.Range("B2:" & alphabet(headerText.Length) & (items.Length + 2).ToString)

    xlRange.Select()
    xlWorkSheet.Paste()

    xlRange.Borders(excel.XlBordersIndex.xlDiagonalDown).LineStyle = excel.XlLineStyle.xlLineStyleNone
    xlRange.Borders(excel.XlBordersIndex.xlDiagonalUp).LineStyle = excel.XlLineStyle.xlLineStyleNone

    With xlRange.Borders(excel.XlBordersIndex.xlEdgeLeft)
        .LineStyle = excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = excel.XlBorderWeight.xlMedium
    End With
    With xlRange.Borders(excel.XlBordersIndex.xlEdgeTop)
        .LineStyle = excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = excel.XlBorderWeight.xlMedium
    End With
    With xlRange.Borders(excel.XlBordersIndex.xlEdgeBottom)
        .LineStyle = excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = excel.XlBorderWeight.xlMedium
    End With
    With xlRange.Borders(excel.XlBordersIndex.xlEdgeRight)
        .LineStyle = excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = excel.XlBorderWeight.xlMedium
    End With
    With xlRange.Borders(excel.XlBordersIndex.xlInsideVertical)
        .LineStyle = excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = excel.XlBorderWeight.xlThin
    End With
    With xlRange.Borders(excel.XlBordersIndex.xlInsideHorizontal)
        .LineStyle = excel.XlLineStyle.xlContinuous
        .ColorIndex = 1 'black
        .TintAndShade = 0
        .Weight = excel.XlBorderWeight.xlThin
    End With

    xlWorkBook.SaveAs(Filename:="C:\Users\Siddharth Rout\Desktop\Word1.xls", FileFormat:=56)
    xlWorkBook.Close()
    xlApp.Quit()

    ReleaseObject(xlRange)
    ReleaseObject(xlWorkSheet)
    ReleaseObject(xlWorkBook)
    ReleaseObject(xlApp)
End Sub


Private Sub ReleaseObject(ByVal obj As Object)
    Try
        Dim intRel As Integer = 0
        Do
            intRel = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
        Loop While intRel > 0
        MsgBox("Final Released obj # " & intRel)
    Catch ex As Exception
        MsgBox("Error releasing object" & ex.ToString)
        obj = Nothing
    Finally
        GC.Collect()
    End Try
End Sub
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Siddharth Rout
  • 147,039
  • 17
  • 206
  • 250
  • @Siddharth - I have tried your solution, that is something I came across a little while ago. For some odd reason, it just isn't releasing it from memory... I do the same thing for exporting to Word and it works great, but Excel it's not. I also reviewed the article you have provided and it's some great information, but still leaves me wondering why it's not releasing this... – Trevor Mar 29 '13 at 06:54
  • 1
    Ok, here's an update. I have noticed when closing my application out, it indeed gets released from memory... I'm not sure why? – Trevor Mar 29 '13 at 06:59
  • That did it @SiddharthRout! I can't believe it was just the GC.Collect() in the finally. Thank You so much, I really appreciate the time and effort you put in to help me with this. Thanks Again! – Trevor Mar 29 '13 at 13:11
  • GC.Collect was also key for getting this working for me. Thank you! – namford Sep 01 '14 at 12:47
  • I figured out that it sometimes depends in which depth Excel is called and that it is better to extract that into another Function and return the value then. For any reason, this was the only version which worked for me. Maybe this helps somebody. – Freddy Oct 14 '15 at 01:33
  • Ok so I step though my code program using your code while watching the task manager processes. While stepping through the code I see the code create the first Excel process but doesn't release it. When it loops around the program again the 2nd,3rd,4th time etc It creates another new excel process and then releases the 2nd one. But Never the first instance. So when I try to open the excel file it say in use unless I force end process in task manager. **Help please** my code [link](http://stackoverflow.com/questions/35318070/excel-workbook-folder-and-picture-files-not-closing-ending-completely) – Duraholiday Feb 10 '16 at 18:47
  • 2
    May i ask about why using byVal instead of byRef, isn't that just release the argument object but not the one that we used? – PSo May 06 '16 at 03:27
  • 1
    @SiddharthRout, according to this post: http://stackoverflow.com/questions/37904483/as-of-today-what-is-the-right-way-to-work-with-com-objects?noredirect=1#comment63779034_37904483, ReleaseComObject is not needed, isn't it? – ehh Jul 04 '16 at 05:52
  • 1
    Would highly recommend NOT using this answer. The user below does not make calls to Marshal and if you use this accepted answer you will be back shortly with the same issue that every other user ran into before you--objects won't be released and you won't know what they are--especially during debugging. Do yourself a favor, ignore the double dot rule, ignore this Marshal call, go directly to `GC.Collect()` and `GC.WaitForPendingFinalizers()` and collect your sanity. Solid answer here: https://stackoverflow.com/questions/25134024/clean-up-excel-interop-objects-with-idisposable/25135685#25135685 – Jacob H Feb 05 '18 at 19:39
  • The `releaseObject()` function here does not do what you think with regards to `obj = Nothing`. Instead, `obj` is passed by value, and thus the original reference in the calling method isn't released. And because that other reference is still set, `GC.Collect()` will not have the desired result. At very least, use `ByRef` to pass `obj`. But really, the whole method reeks of a VB6-era mentality, where in .Net we need to put more trust in the garbage collector to just do it's job: don't set objects to `Nothing` to release them, don't call GC.Collect(). Do use `IDisposable`/`Using`. – Joel Coehoorn Mar 08 '19 at 17:43
3

None of the above recommendations worked for me until I followed @SiddharthRout's comment above. "As of today, what is the right way to work with COM objects?"

It points out that com object references are kept alive under the debugger. A work-around is to call GC from the procedure that calls the com procedure. It worked for me.

Run GC from Finally in a TRY Catch block.

copied from:post by "Govert" on what is the right way to work with COM objects?

using System;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace TestCsCom
{
        Class Program
    {
        static void Main(string[] args)
        {
            // NOTE: Don't call Excel objects in here... 
            //       Debugger would keep alive until end, preventing GC cleanup

            // Call a separate function that talks to Excel
            DoTheWork();

            // Now let the GC clean up (repeat, until no more)
            do
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            while (Marshal.AreComObjectsAvailableForCleanup());
        }

        static void DoTheWork()
        {
            Application app = new Application();
            Workbook book = app.Workbooks.Add();
            Worksheet worksheet = book.Worksheets["Sheet1"];
            app.Visible = true;
            for (int i = 1; i <= 10; i++) {
                worksheet.Cells.Range["A" + i].Value = "Hello";
            }
            book.Save();
            book.Close();
            app.Quit();

            // NOTE: No calls the Marshal.ReleaseComObject() are ever needed
        }
    }
}
Community
  • 1
  • 1
ppete3
  • 31
  • 3
  • 1
    Welcome to StackOverflow. Since links can quickly get outdated/unusable if a URL changes, please try to post the actual code in the solutions here. – cdomination Jul 19 '16 at 18:15
2

I ran into a similar situation and google brought me here. I tried the above answers but none of them worked. But it was good enough to lead me to the correct path.

  1. The ReleaseObject function mention in other answers were not working. Excel kept on running in background.
  2. The GC.Collect + GC.WaitForPendingFinalizers() combo is the way to go, but they must be called outside the function where the excel com objects are defined.

For example the following does not work:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim xlApp As New Excel.Application
    Dim xlWorkBook As Excel.Workbook
    Dim xlWorkSheet As Excel.Worksheet
    xlWorkBook = xlApp.Workbooks.Add
    xlWorkSheet = DirectCast(xlWorkBook.Sheets("sheet1"), Excel.Worksheet)
    
    xlWorkBook.Close()
    xlApp.Quit()

    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

while the following works:

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

    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

Private Sub Button1Proc() Handles Button1.Click
    Dim xlApp As New Excel.Application
    Dim xlWorkBook As Excel.Workbook
    Dim xlWorkSheet As Excel.Worksheet
    xlWorkBook = xlApp.Workbooks.Add
    xlWorkSheet = DirectCast(xlWorkBook.Sheets("sheet1"), Excel.Worksheet)
    
    xlWorkBook.Close()
    xlApp.Quit()
End Sub
1

I have used the ability to close a EXCEL Document in script many times along with hiding making visible and now closing if it's the only workbook open else close this worksheet. Here is my

Sub ExitWorkBook()
Dim wb As Workbook
Dim c As Integer
    c = 0       
    For Each wb In Application.Workbooks
        c = c + 1
    Next wb
    If c = 1 Then
        Application.Quit   '--Quit this worksheet but keep excel open.
    Else
        Workbooks("(excel workbook name).xls").Close    '-- Close Excel
    End If
'
End Sub
Michael Edmison
  • 663
  • 6
  • 14
0

For Each w In Application.Workbooks w.Save Next w Application.Quit

http://msdn.microsoft.com/en-us/library/office/ff839269.aspx

0

I had the same problem. However, the problem only persisted while debugging. All you should need to do is

xlWorkBook.Close
xlApp.Quit

Then just let the code run. You may need to call the Garbage Collector after Button1_Click is finished, but I didn't even need that. It seems to be that stepping through the code or not letting it completely finish throws things off and leaves Excel open.

See Excel Process not closing in VB.net

Community
  • 1
  • 1
Jon
  • 1,189
  • 11
  • 17
  • Thank you! My code was just fine but never occurred to me to tested it without debugging and that was the whole trick. – xfx Dec 31 '15 at 08:45
0

I solved the problem using:

Set xlApp = Nothing

You can check monitoring TaskManager.

farope
  • 61
  • 1
  • 2
0

I found that every instance of a reference to an Excel object had to be explicitly released:

        xlApp = New Excel.Application
        xlWorkBooks = xlApp.Workbooks
        xlWorkBook = xlWorkBooks.Open(Me.txtFilePath.Text)
        xlWorkSheets = xlWorkBook.Worksheets
        xlWorkSheet = CType(xlWorkSheets(1), Excel.Worksheet)

        xlWorkBook.Close()
        xlWorkBooks.Close()
        xlApp.Quit()

        releaseObject(xlWorkSheet)
        xlWorkSheet = Nothing
        releaseObject(xlWorkSheets)
        xlWorkSheets = Nothing
        releaseObject(xlWorkBook)
        xlWorkBook = Nothing
        releaseObject(xlWorkBooks)
        xlWorkBooks = Nothing
        releaseObject(xlApp)
        xlApp = Nothing

    Private Sub releaseObject(ByVal obj As Object)
        Try
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj)
            obj = Nothing
        Catch ex As Exception
            obj = Nothing
        Finally
            GC.Collect()
        End Try
    End Sub
0

I Use This Function After My Work and in The first of the XlApp Call , set the now() date to the FirstDate

Private Sub End_Excel_App_After_Work(ByVal DateStart As Date, ByVal DateEnd As Date)
    Dim xlp() As Process = Process.GetProcessesByName("EXCEL")
    For Each Process As Process In xlp
        If Process.StartTime >= DateStart Then
            If Process.StartTime <= DateEnd Then

                Process.Kill()
                Exit For
            End If
        End If
    Next
    xlp = Process.GetProcessesByName("Microsoft EXCEL")
    For Each Process As Process In xlp
        If Process.StartTime >= DateStart Then
            If Process.StartTime <= DateEnd Then

                Process.Kill()
                Exit For
            End If
        End If
    Next
    xlp = Process.GetProcessesByName("EXCEL.EXE")
    For Each Process As Process In xlp
        If Process.StartTime >= DateStart Then
            If Process.StartTime <= DateEnd Then

                Process.Kill()
                Exit For
            End If
        End If
    Next
End Sub

and After The Process Call this Function. somthing like this:

 Dim FromDT As Date = Now
    Dim xlApp As New Excel.Application
    xlApp = New Excel.Application()
          Dim xlWorkBook As Excel.Workbook
    Dim xlWorkSheet As Excel.Worksheet
     Microsoft.Office.Interop.Excel.Application()

    'End Using
    Try

        xlWorkBook = xlApp.Workbooks.Open("d:\"FIle NAME".xlsX")
        xlWorkSheet = xlWorkBook.Worksheets("WORKSHEET")
      ...CODE BE IN HERE
                   xlWorkBook.Close()
        xlApp.Quit()
        xlApp = Nothing
        ReleaseObject(xlApp)
        ReleaseObject(xlWorkBook)
        ReleaseObject(xlWorkSheet)

        End_Excel_App_After_Work(FromDT, Now)
    Catch ex As Exception
        xlApp.Application.Quit()
        End_Excel_App_After_Work(FromDT, Now)
    End Try
0

I have an issue with this code for releasing objects.

Private Sub ReleaseObject(ByVal obj As Object)
    Try
        Dim intRel As Integer = 0
        Do
            intRel = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
        Loop While intRel > 0
        MsgBox("Final Released obj # " & intRel)
    Catch ex As Exception
        MsgBox("Error releasing object" & ex.ToString)
        obj = Nothing
    Finally
        GC.Collect()
    End Try
End Sub

In the above sub, the object is being passed with ByVal which passes a copy of the object you are trying to release. Kind of pointless. You should be passing ByRef which passes a reference (or a pointer to the object in memory for those familiar with C++) and then the above routine will release the memory being used by the object.

Paul

Paul Smith
  • 51
  • 2
  • 3
-2

Please use this

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()

        Try
            'Dim MSExcelControl() As Process
            Dim iID As Integer
            Dim lastOpen As DateTime
            Dim obj1(10) As Process
            obj1 = Process.GetProcessesByName("EXCEL")
            lastOpen = obj1(0).StartTime
            For Each p As Process In obj1

                If lastOpen < p.StartTime Then
                    iID = p.Id
                    Exit For
                End If

            Next

            For Each p As Process In obj1

                If p.Id = iID Then
                    p.Kill()
                    Exit For
                End If

            Next

        Catch ex As Exception

        End Try

    End Try
End Sub
meires
  • 1