ObjectDisposedException beim Schließen von SerialPort in .Net 2.0

8

Ich habe eine C # Windows Forms-Anwendung, die über einen COM-Port mit einem USB-Dongle kommuniziert. Ich verwende die SerialPort-Klasse in .Net 2.0 für die Kommunikation, und das serielle Port-Objekt ist für die Lebensdauer der Anwendung geöffnet. Die Anwendung sendet Befehle an das Gerät und kann auch unerwünschte Daten vom Gerät empfangen.

Mein Problem tritt auf, wenn das Formular geschlossen wird - ich bekomme (zufällig, leider) eine ObjectDisposedException, wenn ich versuche, den COM-Port zu schließen. Hier ist der Windows-Stack-Trace:

%Vor%

Ich habe Posts von Leuten mit ähnlichen Problemen gefunden und habe den Workaround [hier] [1]

ausprobiert

[1]: Ссылка obwohl das für eine IOException ist und nicht aufgehört hat das Problem.

Mein Close () - Code lautet wie folgt:

%Vor%

Meine Protokollierung hat gezeigt, dass die Ausführung niemals über das Schließen des BaseStream von SerialPort hinausging (dies ist im ersten try -Block), also experimentierte ich mit dem Entfernen dieser Zeile, aber die Ausnahme wird immer noch periodisch ausgelöst - die Anmeldung im zweiten try block erschien dann die Ausnahme passiert ist. Kein catch-Block fängt die Ausnahme ab.

Irgendwelche Ideen?

UPDATE - vollständige Klasse hinzufügen:

%Vor%     
barry 19.01.2012, 14:07
quelle

3 Antworten

28

Hinweis: Die aktuellen Ergebnisse wurden nur mit .NET Framework 4.0 32-Bit unter Windows 7 getestet. Bitte zögern Sie nicht, einen Kommentar zu schreiben, wenn dieser auf anderen Versionen funktioniert.

Bearbeiten: TL; DR: Hier ist der Kern der Problemumgehung. Siehe unten für Erklärungen. Vergessen Sie nicht, SerialPortFixer auch beim Öffnen des SerialPorts zu verwenden. ILog stammt von log4net.

%Vor%

Ich habe in einem kürzlichen Projekt ein paar Tage damit gekämpft.

Es gibt viele verschiedene Fehler (die ich bisher gesehen habe) mit der .NET SerialPort-Klasse, die zu all den Problemen im Web führen.

  1. Die fehlende DCB-Strukturflagge hier: Ссылка Dieser wird von der SerialPortFixer-Klasse festgelegt, Credits gehen an den Autor.

  2. Wenn ein serielles USB-Gerät beim Schließen von SerialPortStream entfernt wird, wird der eventLoopRunner angehalten und SerialPort.IsOpen gibt false zurück. Nach der Beseitigung wird diese Eigenschaft überprüft und das Schließen des internen seriellen Streams wird übersprungen, wodurch das ursprüngliche Handle unbegrenzt offen bleibt (bis der Finalizer läuft, was zum nächsten Problem führt).

    Die Lösung für diesen Fall besteht darin, den internen seriellen Datenstrom manuell zu schließen. Wir können die Referenz von SerialPort.BaseStream abrufen, bevor die Ausnahme aufgetreten ist oder durch Reflektion und erhalte das "internalSerialStream" -Feld.

  3. Wenn ein serielles USB-Gerät entfernt wird, löst das Schließen des internen seriellen Streams eine Ausnahme aus und schließt das interne Handle, ohne dass der eventLoopRunner-Thread beendet wird, was später beim Finalizer des Streams zu einer nicht behebbaren ObjectDisposedException aus dem Hintergrund-Event-Loop-Runner-Thread führt Läuft (was das Auslösen der Ausnahme seltsamerweise verhindert, aber immer noch nicht auf den eventLoopRunner wartet).

    Symptom davon hier: Ссылка

    Die Lösung besteht darin, den Event-Loop-Runner manuell dazu aufzufordern (durch Reflektion) zu stoppen und darauf zu warten, dass er beendet wird, bevor der interne serielle Stream geschlossen wird.

  4. Da Dispose Ausnahmen auslöst, wird der Finalizer nicht unterdrückt. Das ist leicht lösbar:

    GC.SuppressFinalize (Anschluss); GC.SuppressFinalize (port.BaseStream);

Hier ist eine Klasse, die einen seriellen Port umschließt und alle diese Probleme behebt: Ссылка

Mit dieser Workaround-Klasse ist das Zurückkehren zu .NET 1.1 unbehandeltem Ausnahmeverhalten unnötig und es funktioniert mit ausgezeichneter Stabilität.

Dies ist mein erster Beitrag, bitte entschuldigt mich, wenn ich es nicht richtig mache. Ich hoffe es hilft jemandem.

    
Raif Atef 29.01.2012, 21:58
quelle
12

Ja, es gibt einen Fehler in der SerialPort-Klasse, der diese Art von Crash ermöglicht. SerialPort startet einen Thread, wenn Sie Open () aufrufen. Dieser Thread überwacht Ereignisse auf dem Port, so erhalten Sie beispielsweise das DataReceived-Ereignis. Wenn Sie die BaseStream.Close () - oder Close () - oder Dispose () -Methode aufrufen (alle tun dasselbe), fragt nur SerialPort , dass der Thread beendet werden soll, wartet aber nicht darauf, dass er beendet wird .

Dies verursacht alle möglichen Probleme. Eine dokumentierte, man sollte nicht sofort nach dem Schließen einen Port öffnen (). Aber das Missgeschick ist hier, wenn Ihr Programm beendet oder Müll direkt nach dem Aufruf von Close () sammelt. Dadurch wird der Finalizer ausgeführt und es wird versucht, den Handle ebenfalls zu schließen. Es ist immer noch geöffnet, weil der Worker-Thread es immer noch verwendet. Ein Einfädelrennen ist jetzt möglich, dieses ist nicht richtig verzahnt. Der kaboom wird ausgeführt, wenn der Worker das Handle schließen und beenden konnte, kurz bevor der Finalizer-Thread dasselbe versucht. Die Ausnahme ist nicht auffindbar, da sie im Finalizer-Thread auftritt. Die CLR bricht das Programm ab.

Jede Version von .NET seit 2.0 hatte kleine Änderungen in den Klassen, um Probleme mit SerialPort zu umgehen. Mit Abstand das Beste, was Sie tun können, wenn Sie noch in .NET 2.0 sind, ist, nicht tatsächlich Close () aufzurufen. Es passiert automatisch automatisch, der Finalizer kümmert sich darum. Selbst wenn dies aus irgendeinem Grund nicht geschieht (Hard Crash oder Programmabbruch), stellt Windows sicher, dass der Port geschlossen ist.

    
Hans Passant 19.01.2012 17:26
quelle
2

Ich weiß, das ist eine ziemlich alte, aber aktuelle Frage. Ich hatte dieses Problem kürzlich und nachdem ich nach einer Lösung gesucht habe, scheint dieses Problem endlich mit .NET Framework 4.7 behoben worden zu sein. Ссылка

  

Ein Problem in SerialPort wurde behoben, durch das das Entfernen des Geräts während der Ausführung einen Speicherverlust in der SerialStream-Klasse verursachen konnte. [288363]

    
LoriB 27.09.2017 07:32
quelle