Excel richtig beenden

  • VB.NET

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    Excel richtig beenden

    Hallo miteinader,

    gibt es mittlerweile eine richtige Möglichkeit um Excel zu Schließen?
    Excel bleibt bei mir im Hintergund offen und das behindert andere Programme manchmal.

    Das ist mein aktueller Code:

    VB.NET-Quellcode

    1. oExcel = New Excel.Application
    2. oBook = oExcel.Workbooks.Open(TabMatDatenbank,, True)
    3. Suchblatt = "Band"
    4. With oBook.Sheets(Suchblatt)
    5. .
    6. .
    7. .
    8. End With
    9. oBook.Close(False) : oBook = Nothing
    10. oExcel.Quit() : oExcel = Nothing


    Aber danach bleibt immer ein weiterer Process im Taskmanager.

    Ich weiß das es mit .kill eine Möglichkeit gibt aber ist das wirklich die einzigste und richtige Lösung?

    Grüße Felix

    CodeTags korrigiert; bitte zukünftig darauf achten, das richtige CodeHighlighting zu verwenden ~VaporiZed

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    Hallo,
    probier es mal folgendermaßen

    VB.NET-Quellcode

    1. ' ...
    2. oBook.Close(False)
    3. System.Runtime.InteropServices.Marshal.ReleaseComObject(oBook)
    4. oExcel.Quit()
    5. System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcel)


    Edit:
    Falls du mit weiteren Variablen, wie z.B. Worksheets arbeitest, müssen diese ebenfalls über diese Mehtode freigegeben werden.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ISliceUrPanties“ ()

    fiedel93felix schrieb:

    ...leider hilft das nichts.
    .ReleaseComObject() und besser noch .FinalReleaseComObject() ist schon der richtige Ansatz, nur musste das auf jedes einzelne Com-Objekt anwenden, was du in deim Code je angefasst hast.
    Das sind meist mehr als es auf den ersten Blick scheint, und wenn eins vergessen wurde entsteht das beschriebene Fehlverhalten.

    gugge hier: Excel freigeben
    Ich nehme mal, dass trotz deiner „ReleaseComObject“-Statements noch Excel-Anwendungen im Task-Manager zu sehen sind, weil es die alten waren oder, viel eher, weil du, wie „EdR“ erwähnte, nicht alle COM-Objekte freigibst.

    Ich rufe eine asynchrone Methode auf, die die Exceldatei öffnet, bearbeitet, freigibt, und schließt.
    Auch, wenn man für gewöhnlich nicht „GC.Collect“ verwenden sollte, ist es laut Stack Overflow aber bei COM-Objekten legitim. Also tue ich, nachdem die Methode durch ist, nochmal mit „GC.Collect“ alles aufräumen.
    Dann kann es noch ein paar Sekunden dauern, bis man die Excelanwendung nicht mehr im Task-Manager sieht.

    Benutze diesen Code:

    C#-Quellcode

    1. private static void Save_close_and_release_file(string filePath, ref Application xlApp, ref Workbook workbook, ref Worksheet sheet)
    2. {
    3. try
    4. {
    5. sheet.SaveAs(filePath, XlFileFormat.xlWorkbookDefault);
    6. }
    7. catch (System.Runtime.InteropServices.COMException)
    8. { }
    9. workbook.Close();
    10. xlApp.Quit();
    11. if (sheet != null)
    12. {
    13. System.Runtime.InteropServices.Marshal.ReleaseComObject(sheet);
    14. }
    15. if (workbook != null)
    16. {
    17. System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
    18. }
    19. if (xlApp != null)
    20. {
    21. System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
    22. }
    23. sheet = null;
    24. workbook = null;
    25. xlApp = null;
    26. }


    Viele Grüße
    PS: Der Try-Catch-Block ist für den Fall da, dass es die Datei mit demselben Namen bereits gibt. Excel fragt dich dann, ob du die Datei überschreiben möchtest. Sollte der Nutzer „Nein“ auswählen, wird eine Exception geworfen. Damit dein Programm nicht abstürzt, steht hier Try-Catch.
    Vielen Dank für die ganzen Antworten aber mit dem richtigem Freigeben von Excel das klappt nicht bzw ist mir das zu aufwendig jede angesprochene Zelle freizugeben.

    Ich mach es jetzt etwas fraglich aber funktional:
    Ich Schließes Excel und anschließend benutze ich noch das .Kill event.
    Durch diese Kombi verschwindet der Process und ich erhalte keine Meldung beim nächsten Excelstart, dass einige Dateien nicht richtig geschlossen wurden.

    VB.NET-Quellcode

    1. Protected Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Integer, ByRef lpdwProcessId As IntPtr) As IntPtr
    2. Try
    3. If TabMatGef = False Then
    4. oBook.Close(False)
    5. oExcel.Quit()
    6. Dim processId As IntPtr
    7. GetWindowThreadProcessId(oExcel.Hwnd, processId)
    8. Dim xLProcess As Process = Process.GetProcessById(processId.ToInt32())
    9. xLProcess.Kill()
    10. oBook = Nothing
    11. oExcel = Nothing
    12. End If
    13. Catch exx As Exception
    14. End Try


    CodeTags korrigiert; bitte zukünftig darauf achten, das richtige CodeHighlighting zu verwenden ~VaporiZed

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    Aufwand ist an dieser Stelle eben vorgesehen, weil das Aufräumen an sich ein wichtiger Punkt ist, und bei Excel nicht automatisch funktioniert.

    Ich verwende Excel in einer IDisposable Klasse. Hier wird ein gewisser Umfang an Elementen definiert (Felder). Die können in IrgendwasSinnvolles() genutzt werden, und dann werden sie alle aufgeräumt Dispose()
    Die Struktur der Schnittstelle ist zurecht geschnitten auf eventuelle Eventualitäten. Es geht natürlich auch ohne Struktur, aber die Idee bleibt gleich, jeder Excel.-Member soll aufgeräumt werden.

    Wie du siehst gibt es da eine List(Of Ranges) und da wird in einer Schleife jede Range (Ansammlung von Zellen) aufgeräumt.
    MeineKlasse

    VB.NET-Quellcode

    1. Friend Class ExcelHelper
    2. Implements IDisposable
    3. Private disposedValue As Boolean
    4. Private _excel As Excel.Application
    5. Private _Books As Excel.Workbooks
    6. Private _Book As Excel.Workbook
    7. Private _Sheets As Excel.Sheets
    8. Private ReadOnly _WSheets As List(Of Excel.Worksheet)
    9. Private _Sheet As Excel.Worksheet
    10. Private ReadOnly _Ranges As List(Of Excel.Range)
    11. Private _Range As Excel.Range
    12. Friend Sub New()
    13. _excel = New Excel.Application()
    14. _WSheets = New List(Of Excel.Worksheet)
    15. _Ranges = New List(Of Excel.Range)
    16. End Sub
    17. Friend Sub IrgendwasSinnvolles()
    18. '...
    19. End Sub
    20. 'eventuell mehr Methoden
    21. Protected Overridable Sub Dispose(ByVal disposing As Boolean)
    22. If Not disposedValue Then
    23. If disposing Then
    24. 'Managed Disposes here
    25. End If
    26. If _Range IsNot Nothing Then
    27. Marshal.FinalReleaseComObject(_Range)
    28. _Range = Nothing
    29. End If
    30. If _Ranges.Count > 0 Then
    31. For Each rng In _Ranges
    32. Marshal.FinalReleaseComObject(rng)
    33. Next
    34. _Ranges.Clear()
    35. End If
    36. If _Sheet IsNot Nothing Then
    37. Marshal.FinalReleaseComObject(_Sheet)
    38. _Sheet = Nothing
    39. End If
    40. If _WSheets.Count > 0 Then
    41. For Each sht In _WSheets
    42. Marshal.FinalReleaseComObject(sht)
    43. Next
    44. _WSheets.Clear()
    45. End If
    46. If _Sheets IsNot Nothing Then
    47. Marshal.FinalReleaseComObject(_Sheets)
    48. _Sheets = Nothing
    49. End If
    50. If _Book IsNot Nothing Then
    51. Marshal.FinalReleaseComObject(_Book)
    52. _Book = Nothing
    53. End If
    54. If _Books IsNot Nothing Then
    55. Marshal.FinalReleaseComObject(_Books)
    56. _Books = Nothing
    57. End If
    58. If _excel IsNot Nothing Then
    59. _excel.Quit()
    60. Marshal.FinalReleaseComObject(_excel)
    61. _excel = Nothing
    62. End If
    63. disposedValue = True
    64. End If
    65. End Sub
    66. Protected Overrides Sub Finalize()
    67. Dispose(disposing:=False)
    68. End Sub
    69. Friend Sub Dispose() Implements IDisposable.Dispose
    70. Dispose(disposing:=True)
    71. GC.SuppressFinalize(Me)
    72. End Sub
    73. End Class

    Dann nutzt sich das aber recht simpel:

    VB.NET-Quellcode

    1. Dim Excel As New ExcelHelper
    2. Try
    3. Excel.IrgendwasSinnvolles()
    4. Finally
    5. Excel.Dispose()
    6. End Try
    7. ' oder
    8. Using Excel as New ExcelHelper
    9. Excel.IrgendwasSinnvolles()
    10. End Using

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Hallo,
    ich habe jetzt mal versucht Excel "richtig" freizugeben und zu schließen aber das klappt bei mir nicht.
    Der Code von Haudruferzappeltnoch funktioniert nicht, daher hab ich selber nochmal geschaut und probiert.
    Mein Code beendet Excel aber auch nicht richtig.

    VB.NET-Quellcode

    1. Public oExcel As Excel.Application
    2. Public oBook As Excel.Workbook
    3. Public oBooks As Excel.Workbooks
    4. Public oSheets As Excel.Sheets
    5. Public oSheet As Excel.Worksheet
    6. Public oRange As Excel.Range
    7. Private Sub ButWerteermitteln_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButWerteermitteln.Click
    8. Try
    9. MatTNrBez = ""
    10. MatTNrVerf = ""
    11. ComboMatRNr.Items.Clear()
    12. ComboMatRNr.Text = ""
    13. LabMatRNrVerfügbar.Text = ""
    14. Dim InhaltZelle As String = ""
    15. Dim HilfRNummer As String = txtMatRNr.Text.Replace(" ", "")
    16. Suchbegriff_find = HilfRNummer
    17. Dim RNummergefunden As Boolean = False
    18. oExcel = New Excel.Application
    19. oBooks = oExcel.Workbooks
    20. oBook = oExcel.Workbooks.Open(TabMatDatenbank,, True)
    21. oSheets = oBook.Worksheets
    22. Suchblatt = "Band"
    23. oSheet = oBook.Sheets(Suchblatt)
    24. With oSheet 'oBook.Sheets(Suchblatt)
    25. If Not .Range("A4:A4000").Find(What:=Suchbegriff_find) Is Nothing Then
    26. Gef = .Range("A4:A4000").Find(What:=Suchbegriff_find).Address
    27. oRange = .Range("A4:A4000").Find(What:=Suchbegriff_find)
    28. Marshal.ReleaseComObject(oRange)
    29. Zeile = CDec(Replace(Gef, "$A$", ""))
    30. ComboMatRNr.Items.Add(HilfRNummer & ": " & .Cells(Zeile, 2).Value())
    31. oRange = .Cells(Zeile, 2)
    32. Marshal.ReleaseComObject(oRange)
    33. ComboMatRNr.SelectedIndex = 0
    34. LabMatRNrVerfügbar.Text = "verfügbar " & HilfRNummer & "= " & .Cells(Zeile, 30).Value() & .Cells(Zeile, 4).Value()
    35. oRange = .Cells(Zeile, 4)
    36. Marshal.ReleaseComObject(oRange)
    37. oRange = .Cells(Zeile, 30)
    38. Marshal.ReleaseComObject(oRange)
    39. RNummergefunden = True
    40. Else
    41. End If
    42. End With
    43. Marshal.ReleaseComObject(oSheet)
    44. Marshal.ReleaseComObject(oSheets)
    45. oBook.Close(False)
    46. oBooks.Close()
    47. Marshal.ReleaseComObject(oBook)
    48. Marshal.ReleaseComObject(oBooks)
    49. oExcel.Quit()
    50. Marshal.ReleaseComObject(oExcel)
    51. MsgBox("Fertig")
    52. Catch ex As Exception
    53. LabMatRNrVerfügbar.Text = "Fehler beim ermitteln der Daten für die Eingegebene Teilenummer!!"
    54. End Try
    55. Exit Sub
    Du musst noch kleinschrittiger arbeiten, denke ich.

    Du hast da z.B. eine Abfrage im With Block: oSheet.Range("A4:A4000").Find(What:=Suchbegriff_find)

    Dieses oSheet.Range("A4:A4000") fliegt jetzt irgendwo frei im Raum rum und du kannst es nicht mehr ansprechen und auch nicht freigeben.

    Zuerst musst du also oRange = oSheet.Range("A4:A4000") definieren. Dann kannst du .Find benutzen. Das musst du auch wieder zuweisen damit du es freigeben kannst, das ist nämlich selbst wieder eine Range.

    Deswegen nutze ich bei mir eine List(Of Excel.Range). Man hat davon üblicherweise ja mehrere. Dann kann man nach und nach neue anlegen und am Ende gehen alle raus, wenn die Schleife drüberläuft. (Mit nem Dictionary kann man denen auch Namen geben, wenn man es mit der normalen Auflistung nicht mehr überblicken kann.)

    Nebenbei
    Alternativ könnte man auch oRange erst freigeben und dann nochmal verwenden.

    VB.NET-Quellcode

    1. oRange = fooSheet.Range("A1:A2")
    2. Marshal.ReleaseComObject(oRange)
    3. oRange = fooSheet.Range("B1:B2")
    4. Marshal.ReleaseComObject(oRange)

    Du brauchst aber mindestens zwei Range Objekte gleichzeitig, daher brauchst du auch mehrere Variablen


    Außerdem aufgepasst bei oBook.Sheets und oBook.Worksheets das sind zwei verschiedene Dinge. Statt den Sheets verwende ich eine List(Of Worksheet), ich weiß schon gar nicht mehr was es damit auf sich hat eins ist nur ein bestimmter Teil der Arbeitsblätter. Jedenfalls kann es genauso wie bei der Range hier sein, dass das oBook.Sheets Objekt frei umherirrt.

    Ich glaub du versuchst auch bei den .Cells(...), eine Range die du genutzt hast, nachträglich einer Variablen zuzuweisen. Das musst du natürlich vorher machen und dann mit der Variablen arbeiten. Jedes .Cells ist ein neues Range-Objekt selbst wenn die gleichen Daten drinstehen oder die Daten von derselben Stelle geholt werden.
    Ich denke du solltest die ganze Range aus dem Sheet, die du brauchst in ein Array o.Ä. laden. Dann musst du nur eine einzige Range entsorgen und mit dem Array kannst du auf sauberen Grund arbeiten ohne dir über das Aufräumen Gedanken zu machen

    Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Ich hbae jetzt nochmal alle Rang definiert vorm der Nutzung und danach freigeben über .FinalReleaseComObject.
    Und immernoch bleibt Excel.

    VB.NET-Quellcode

    1. Private Sub ButWerteermitteln_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButWerteermitteln.Click
    2. Try
    3. MatTNrBez = ""
    4. MatTNrVerf = ""
    5. ComboMatRNr.Items.Clear()
    6. ComboMatRNr.Text = ""
    7. LabMatRNrVerfügbar.Text = ""
    8. Dim InhaltZelle As String = ""
    9. Dim HilfRNummer As String = txtMatRNr.Text.Replace(" ", "")
    10. Suchbegriff_find = HilfRNummer
    11. Dim RNummergefunden As Boolean = False
    12. oExcel = New Excel.Application
    13. oBooks = oExcel.Workbooks
    14. oBook = oExcel.Workbooks.Open(TabMatDatenbank,, True)
    15. oSheets = oBook.Worksheets
    16. Suchblatt = "Band"
    17. oSheet = oBook.Sheets(Suchblatt)
    18. With oSheet 'oBook.Sheets(Suchblatt)
    19. oRange = oSheet.Range("A4:A4000")
    20. If Not oRange.Find(What:=Suchbegriff_find) Is Nothing Then
    21. Gef = oRange.Find(What:=Suchbegriff_find).Address
    22. oRange = oRange.Find(What:=Suchbegriff_find)
    23. Marshal.FinalReleaseComObject(oRange)
    24. Zeile = CDec(Replace(Gef, "$A$", ""))
    25. oRange = .Cells(Zeile, 2)
    26. ComboMatRNr.Items.Add(HilfRNummer & ": " & oRange.Value())
    27. Marshal.FinalReleaseComObject(oRange)
    28. ComboMatRNr.SelectedIndex = 0
    29. oRange = .Cells(Zeile, 30)
    30. LabMatRNrVerfügbar.Text = "verfügbar " & HilfRNummer & "= " & oRange.Value()
    31. Marshal.FinalReleaseComObject(oRange)
    32. oRange = .Cells(Zeile, 4)
    33. LabMatRNrVerfügbar.Text = LabMatRNrVerfügbar.Text & oRange.Value()
    34. Marshal.FinalReleaseComObject(oRange)
    35. RNummergefunden = True
    36. Else
    37. End If
    38. End With
    39. Marshal.FinalReleaseComObject(oSheet)
    40. Marshal.FinalReleaseComObject(oSheets)
    41. oBook.Close(False)
    42. oBooks.Close()
    43. Marshal.FinalReleaseComObject(oBook)
    44. Marshal.FinalReleaseComObject(oBooks)
    45. oExcel.Quit()
    46. Marshal.FinalReleaseComObject(oExcel)
    47. MsgBox("Fertig")
    48. Catch ex As Exception
    49. LabMatRNrVerfügbar.Text = "Fehler beim ermitteln der Daten für die Eingegebene Teilenummer!!"
    50. End Try
    51. Exit Sub
    Du machst es Dir aber auch nicht leicht.
    Wie wär es mit Schritt für Schritt? Erstmal die Zeilen#15 bis #48 auskommentieren, sodass nur noch Excel/oExcel erstellt, geschlossen, beendet und freigegeben wird.
    -> Wenn das nicht klappt, dann brauchst Du gar nicht weiterzusuchen.
    -> Wenn das aber doch klappt, dann oBooks dazuschalten und ausprobieren.
    Und so weiter, bis Du das Problem eingekesselt hast.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.