marcoborn
Goto Top

Prüfen, ob Programm schon disposed wurde

Hallo Forum,
ich habe in VB.NET ein Programm geschrieben, welches Word startet und dort Daten ausliest. Obwohl ich die Variable, die auf die Word-Applikation verweist, am Ende auf Nothing setze und dispose, bleibt Word im Task-Manager als Prozess weiterhin aktiv. Ich habe dann versucht, das Fenster zu maximieren, damit der Nutzer es manuell schließen kann, aber dann bricht die Prozedur ab.

Word ist weiterhin offen, aber mein Programm hat die Verbindung verloren und kann nicht mehr mit Word interagieren.
Wie kann ich entweder Word sauber beenden oder prüfen, ob es im Task-Manager noch als Prozess läuft und diesen dann sauber beenden (d.h. ggf. mit Möglichkeit zum Speichern)?

Vielen Dank im voraus,
M. Born

Content-ID: 496325

Url: https://administrator.de/contentid/496325

Ausgedruckt am: 24.11.2024 um 14:11 Uhr

140913
140913 19.09.2019 aktualisiert um 13:47:15 Uhr
Goto Top
Zitat von @MarcoBorn:

Hallo Forum,
ich habe in VB.NET ein Programm geschrieben, welches Word startet und dort Daten ausliest. Obwohl ich die Variable, die auf die Word-Applikation verweist, am Ende auf Nothing setze und dispose, bleibt Word im Task-Manager als Prozess weiterhin aktiv. Ich habe dann versucht, das Fenster zu maximieren, damit der Nutzer es manuell schließen kann, aber dann bricht die Prozedur ab.

Word ist weiterhin offen, aber mein Programm hat die Verbindung verloren und kann nicht mehr mit Word interagieren.
Wie kann ich entweder Word sauber beenden oder prüfen, ob es im Task-Manager noch als Prozess läuft und diesen dann sauber beenden (d.h. ggf. mit Möglichkeit zum Speichern)?
Wird Word über ein COM-Object angesprochen?, für die gibt's Extra Behandlung:
System.Runtime.InteropServices.Marshal.ReleaseComObject(DeinWordObject)
https://docs.microsoft.com/de-de/dotnet/api/system.runtime.interopservic ...

Und vorher am Ende immer ein Quit() auf das Word-Application Object absetzen damit Word "sanft" schließt face-wink und nicht einfach nur abgewürgt wird.
MarcoBorn
MarcoBorn 19.09.2019 um 13:58:48 Uhr
Goto Top
Wenn ich statt Quit den o.g. Befehl aufrufe, erhalte ich eine Fehlermeldung:
Der Objekttyp muss __ComObject sein oder von __ComObject abgeleitet werden.
Parametername: o
   bei System.Runtime.InteropServices.Marshal.ReleaseComObject(Object o)

Muss ich Word ggf. anders definieren, oder reicht folgendes?
<CLSCompliant(False)> Friend Shared gObjDOKAppWd As Word.Application
gObjDOKAppWd = New Word.Application
emeriks
emeriks 19.09.2019 aktualisiert um 14:03:08 Uhr
Goto Top
Hi,
Zitat von @MarcoBorn:
Obwohl ich die Variable, die auf die Word-Applikation verweist, am Ende auf Nothing setze und dispose, bleibt Word im Task-Manager als Prozess weiterhin aktiv. Ich habe dann versucht, das Fenster zu maximieren, damit der Nutzer es manuell schließen kann, aber dann bricht die Prozedur ab.
Also das Word.Application Objekt hat meines Wissens keine Dispose-Methode. Weiterhin - für andere Objekte - muss man natürlich erst das Dispose aufrufen (sofern für diese Klasse verfügbar) und dann erst die Variable auf Nothing setzen.

Wie @140913 schon schreibt, muss man das Word-Programm schon explizit über Quit() schließen, weil es als eigenständiger Prozess gestartet wird.

E.
emeriks
emeriks 19.09.2019 um 14:04:47 Uhr
Goto Top
Selbst wenn Du das ReleaseComObject absetzt, bleibt sollte die Anwendung weiterhin als Prozess laufen.
MarcoBorn
MarcoBorn 19.09.2019 um 14:06:40 Uhr
Goto Top
Gibt es dann keine Möglichkeit, Word wieder zu beenden?
emeriks
emeriks 19.09.2019 aktualisiert um 14:23:19 Uhr
Goto Top
Zitat von @MarcoBorn:
Gibt es dann keine Möglichkeit, Word wieder zu beenden?
Liest Du unsere Kommentare nicht?
Methode Quit() ausführen! Was sollen wir da noch schreiben?

Edit:
Natürlich bevor Du die Variable auf Nothing setzt, ist klar.
140913
140913 19.09.2019 aktualisiert um 14:23:06 Uhr
Goto Top
Zitat von @MarcoBorn:

Gibt es dann keine Möglichkeit, Word wieder zu beenden?
Lesen ist schon schwer, stand oben schon in meinem ersten Kommentar.
https://docs.microsoft.com/de-de/office/vba/api/word.application.quit(me ...
MarcoBorn
MarcoBorn 19.09.2019 um 14:24:25 Uhr
Goto Top
Quit und auf Nothing setzen hatte ich ja in meinem ursprünglichen Code schon drin, aber trotzdem ist Word selbst 30 Minuten nach dem Ende des Programms immer noch aktiv. Selbst der GC sollte zwischendurch mindestens einmal durchgelaufen sein. Die Stelle mit Quit und Nothing-setzen wird auch definitiv durchlaufen. Das habe ich testweise mit eine MsgBox davor und danach getestet.

ReleaseComObject bringt mir nur eine Fehlermeldung, hilft mir also leider auch nicht weiter. Was könnte ich noch probieren?
140913
140913 19.09.2019 aktualisiert um 14:29:04 Uhr
Goto Top
ReleaseComObject bringt mir nur eine Fehlermeldung
Das ist auch nur mögich wenn Word als reines COM-Object erzeugt wurde, nicht mit managed classes.
aber trotzdem ist Word selbst 30 Minuten nach dem Ende des Programms immer noch aktiv.
Dann wirst du noch andere Referenzen offen haben.
Selbst der GC sollte zwischendurch mindestens einmal durchgelaufen sein.
Den mal testweise die Garbage Collection manuell aufrufen.
MarcoBorn
MarcoBorn 19.09.2019 um 14:28:14 Uhr
Goto Top
Hier mal mein Code zum Schließen:
With gObjDOKAppWd
  'Word-Datei schließen und Word beenden  
  .DisplayAlerts = Word.Enums.WdAlertLevel.wdAlertsNone
  .ActiveDocument.Close(Word.Enums.WdSaveOptions.wdDoNotSaveChanges)
  .Visible = True
  .Quit(Word.Enums.WdSaveOptions.wdDoNotSaveChanges)
End With
emeriks
emeriks 19.09.2019 aktualisiert um 14:34:48 Uhr
Goto Top
Nochmal:
Word läuft als eigener Prozess! Da nützt Dir also "Nothing" und "Garbage Collection" für das Beenden des Prozess überhaupt nichts, weil es sich nur auf die belegten Ressourcen innerhalb Deines Programms bezieht.

Wenn der Prozess nach dem Quit() immer noch läuft, dann bekommst Du im VB.Net doch sicher eine Ausnahme gefeuert, oder?
140913
140913 19.09.2019 aktualisiert um 14:31:00 Uhr
Goto Top
.DisplayAlerts = Word.Enums.WdAlertLevel.wdAlertsNone
Dir ist aber schon klar das du damit sämtliche Meldungen auch nach dem Schließen von Word permanent deaktivierst?
Das sollte man vor dem Schließen der Instanz wieder zurücksetzen.
MarcoBorn
MarcoBorn 19.09.2019 um 14:30:39 Uhr
Goto Top
Nein, der Code läuft normal durch. Ich habe alle Prozeduren komplett mit Try-Catch gekapselt, aber ich erhalte keine Fehlermeldung. Word wird auch nur einmal zu Anfang des Projekts gestartet.
MarcoBorn
MarcoBorn 19.09.2019 um 14:41:56 Uhr
Goto Top
Danke für den Hinweis. Nach dem Schließen der Datei setze ich es wieder auf "All". Ich lasse gerade das Tool durchlaufen. Mal sehen, ob mir jetzt etwas anderes angezeigt wird.
SachsenHessi
SachsenHessi 19.09.2019 aktualisiert um 15:00:57 Uhr
Goto Top
Hallo,

dein Problem besteht nicht nur bei Word, sonder auch bei Excel.
Bei beiden arbeitet die Quit()-Methode nicht sauber. Man müsste jetzt über die Marshalling gehen, aber das nervt und ist aufwendig.
(Stichworte: System.Runtime.InteropServices.Marshal und System.Runtime.InteropServices.GCHandle)

Folgendes ist zwar nicht "schön", aber funktioniert und ist schnell und einfach.
Der Ansatz ist einfach den Prozess für die Office-Instanz zu killen.
Das hier sollte auch für Word nutzbar sein (ist für Excel):
    'Um Instanz am ende "abzuschießen",  
    'da das Quit/Close nicht korrekt arbeitet  
    Public Property procProcessesBeforeStart As Diagnostics.Process()
    Public Property procProcessesAfterStart As Diagnostics.Process()
    Public Property procProcessId4Excel As Int64 = -1  'Um Instanz am ende "abzuschießen"  
	
    ''' <summary>  
    ''' Gibt die ProcessID der aktiven gerade erstellten Excel Instanz zurück  
    ''' </summary>  
    ''' <returns></returns>  
    Public Function GetExcelProcessID() As Int64

        Dim _intID As Int64 = -1
        procProcessId4Excel = -1
        For Each _itemAfter As Process In procProcessesAfterStart
            Dim _bolDrin As Boolean = False
            For Each _itemBefore As Process In procProcessesBeforeStart
                If _itemBefore.Id = _itemAfter.Id Then
                    _bolDrin = True
                Else
                    _bolDrin = False
                End If
            Next
            If _bolDrin Then
                Continue For
            Else
                _intID = _itemAfter.Id
                Exit For
            End If
        Next
        Return _intID
    End Function


    ''' <summary>  
    ''' Killt diese Instanz von Excel endgültig aus dem Speicher  
    ''' </summary>  
    Public Sub KillExcelThisInstanz(Optional _bolShowError As Boolean = False)
        Try
            If procProcessId4Excel >= 0 Then
                Dim _procExcel As Process = Process.GetProcessById(procProcessId4Excel)
                _procExcel.Kill()
            End If
        Catch ex As Exception
            'hier Fehlerhandling  
        End Try
    End Sub
	
    Public Sub Beispiel()
	    procProcessesBeforeStart = Diagnostics.Process.GetProcessesByName("Excel") '<---- !!   
            Dim objXLApp As New Microsoft.Office.Interop.Excel.Application
            Dim objXLSheet As Microsoft.Office.Interop.Excel.Worksheet
            Dim objXLRange As Microsoft.Office.Interop.Excel.Range
            procProcessesAfterStart = Diagnostics.Process.GetProcessesByName("Excel") '<----- !!  
            procProcessId4Excel = GetExcelProcessID()  '<--- ProzessID der aktuellen Instanz  
            objXLApp.Workbooks.Add("xxxxxxxx")  
            objXLApp.Visible = False
            objXLApp.WindowState = Microsoft.Office.Interop.Excel.XlWindowState.xlNormal
            objXLSheet = objXLApp.Workbooks(1).Sheets(1)
            objXLRange = objXLSheet.Range("C2")  
            objXLRange.Value = "blahblah"  
            If IO.File.Exists("xxxxxxx") Then IO.File.Delete("xxxxxxx")  
            objXLApp.Workbooks(1).SaveAs("xxxxxxxxxx", Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookDefault)  
            objXLApp.Quit()
            KillExcelThisInstanz()  '<---- und heier wird diese Instanz wider gekillt  
	end sub
MarcoBorn
MarcoBorn 19.09.2019 um 15:06:39 Uhr
Goto Top
Danke. Hier wird zwar Word nicht sauber beendet, sondern nur abgeschossen. Aber ich werde mal testen, ob das zumindest als Notlösung funktioniert.
SachsenHessi
SachsenHessi 19.09.2019 um 15:25:40 Uhr
Goto Top
Hatte ich geschrieben, nicht "schön" aber funktioniert.
Selbst nach einem
Quit()
Marshal.ReleaseComObject()
GCCollect()
GC.WaitForPendingFinalizers()
war es teilweise (besser häufiger) noch aktiv.
daher diese frickel-lösung.

Gruß
SH