System.ObjectDisposedException: Safe handle has been closed

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    System.ObjectDisposedException: Safe handle has been closed

    Hallo zusammen,

    Ich habe ein Problem bezüglich der ObjectDisposedException, welches mir auf Anhieb nicht ersichtlich ist, wieso ich diese erhalte.

    Situation:
    Ich habe ein Xamarin Cross-Platform App, welche über eine API eine Verbindung zum Server aufbauen soll. Der Verbindungsaufbau wird dabei in einem extra Thread ausgeführt.

    Rufe ich nun den Konstruktor der Schnittstelle auf, welcher sich automatisch verbindet, und der Verbindungsaufbau schlägt fehl, weil der Server derzeit nicht läuft, dann erhalte ich am Ende des Connect Threads die besagte Exception.

    Beim verbinden wird die Schnittstelle IAsyncResult verwendet, um asynchon die Verbindung aufzubauen.
    Der Aufruf der Connect-Funktion findet dabei im Konstruktor des AufgabenApi Objects statt.

    Hier einmal der Konstruktor der AufgabenApi:

    C#-Quellcode

    1. /// <summary>
    2. /// Erstellt eine neue Instanz der AufgabenApi und verbindet sich mit dem angegeben Server.
    3. /// </summary>
    4. /// <param name="host"></param>
    5. /// <param name="port"></param>
    6. /// <exception cref="ArgumentException">Tritt auf, wenn der Port kleine als 0 oder größer als 99999 ist.</exception>
    7. /// <exception cref="Exceptions.ConnectionRefusedException">Tritt auf, wenn die Verbindung zum Server aufgrund von falschen Parametern nicht hergestellt werden konnte.</exception>
    8. public AufgabenApi(string host, int port)
    9. {
    10. this.Port = port;
    11. this.Host = host;
    12. Connect();
    13. }


    Die Connect Methode der Api sieht wie folgt aus:

    C#-Quellcode

    1. /// <summary>
    2. /// Stellt eine Verbindung zum AufgabenServer her.
    3. /// </summary>
    4. /// <returns>True wenn die Verbindung hergestellt wurde, ansonsten false.</returns>
    5. public bool Connect()
    6. {
    7. if (this.IsConnected) // Wenn die Verbindung bereits besteht, dann muss nicht erneut connected werden!
    8. return true;
    9. return GlobaleVariabeln.srv.Connect(Host, Port);
    10. }


    Die Funktion GlobaleVariabeln.srv.Connect():

    C#-Quellcode

    1. /// <summary>
    2. /// Verbindet sich mit dem angegebenen Server
    3. /// </summary>
    4. /// <param name="_host">Der Serverhost, entweder als IP oder als Name</param>
    5. /// <param name="_port">Der Port</param>
    6. /// <param name="timeout">Timeout in MS für den Verbidnungsaufbau</param>
    7. /// <exception cref="Exceptions.InvalidProtokollException">Tritt auf, </exception>
    8. /// <returns>True bei erfolgreicher Verbindnung</returns>
    9. public bool Connect(string _host, int _port, int timeout = 3000)
    10. {
    11. // Verbindung aufbauen
    12. #if FixServer
    13. _host = "WKS68"; // Wenn wir was testen wollen, läuft der Server sehr wahrscheinlich auf dem Entwicklerrechner, dieser wird hier angegeben
    14. // Bei Test in VM Muss der Port zur VM Weitergeleitet werden!!! Host bleibt dabei gleich!!!
    15. // Siehe https://blogs.oracle.com/scoter/networking-in-virtualbox-v2#NAT-PF für VirtualBox
    16. //_host = "127.0.0.1";
    17. #endif
    18. // Wir holen alle IPv4 Adressen des Hosts
    19. IPAddress[] hostIPs;
    20. try
    21. {
    22. hostIPs = Dns.GetHostEntry(_host).AddressList.Where(addr => addr.AddressFamily == AddressFamily.InterNetwork).ToArray();
    23. }
    24. catch (Exception)
    25. {
    26. // Der Host ist unbekannt!
    27. return false;
    28. }
    29. //hostIPs = hostIPs.Reverse().ToArray(); // Zum testen einer anderen IP am Start
    30. try
    31. {
    32. foreach (IPAddress IP in hostIPs)
    33. {
    34. connection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    35. Debug.WriteLine("Verbinden mit {0}:{1}", _host, _port);
    36. IAsyncResult ar = connection.BeginConnect(IP, _port, null, connection);
    37. using (ar.AsyncWaitHandle)
    38. {
    39. if (ar.AsyncWaitHandle.WaitOne(timeout, true))
    40. {
    41. Debug.WriteLine("Verbunden mit {0}:{1}", IP, _port);
    42. }
    43. }
    44. if (IsConnected) // Prüfen ob die Verbindung noch besteht, da Virtuelle Netzwerkadpater ggf. alles annehmen, aber dann nicht verwalten und direkt disconnecten
    45. break;
    46. connection.Close();
    47. }
    48. }
    49. catch (SocketException ex)
    50. {
    51. if (ex.HResult == -2147467259) // Server hat die Verbindung abgelehnt, ggf. falsche Einstellungen!!
    52. {
    53. throw new Exceptions.ConnectionRefusedException("Die Verbindung zum AufgabenServer ist fehlgeschlagen. Bitte prüfen Sie die Verbindungseinstellungen!", _host, _port);
    54. }
    55. Debug.WriteLine("Verbindung abgelehnt");
    56. return false;
    57. }
    58. if (IsConnected)
    59. {
    60. empfangsThread = new Thread(new ThreadStart(Empfange))
    61. {
    62. Name = "Empfangsthread ServerConnection" // Wird angegeben, damit beim Debuggen der Thread leichter zugeordnet werden kann
    63. };
    64. empfangsThread.Start();
    65. Version serverProtokollVersion = null;
    66. ManualResetEvent ewh = new ManualResetEvent(false);
    67. ProtokollEventHandler handler = (object sender, ProtokollEventArgs e) =>
    68. {
    69. serverProtokollVersion = e.ProtokollVersion;
    70. ewh.Set();
    71. };
    72. this.ProtokollReceived += handler;
    73. // Protokoll initialisieren
    74. ServerProtokoll serverProtokoll = new ServerProtokoll()
    75. {
    76. ProtokollNachricht = new NachrichtenTypen.ProtokollNachricht()
    77. };
    78. serverProtokoll.Send();
    79. ewh.WaitOne();
    80. this.ProtokollReceived -= handler;
    81. ewh.Close();
    82. if (serverProtokollVersion > GlobaleVariabeln.ProtokollVersion)
    83. throw new Exceptions.InvalidProtokollException("Die Protokollversion des Clients ist veraltet. Bitte führen Sie ein Update der Aufgabenlibrary durch.");
    84. else if (serverProtokollVersion < GlobaleVariabeln.ProtokollVersion)
    85. throw new Exceptions.InvalidProtokollException("Die Protokollversion des Servers ist veraltet. Bitte führen Sie ein Update der Aufgabenlibrary durch.");
    86. OnConnected(EventArgs.Empty);
    87. }
    88. return IsConnected;
    89. }


    die Property IsConnected:

    C#-Quellcode

    1. /// <summary>
    2. /// Ruft einen Wert ab, der angibt, ob die Verbindung zum Server besteht.
    3. /// </summary>
    4. public bool IsConnected
    5. {
    6. get
    7. {
    8. try // Wir Try-Catchen, weil beim Receive ggf. die Verbindung abbrechen kann!
    9. {
    10. if (connection != null && connection.Connected)
    11. {
    12. /* pear to the documentation on Poll:
    13. * When passing SelectMode.SelectRead as a parameter to the Poll method it will return
    14. * -either- true if Socket.Listen(Int32) has been called and a connection is pending;
    15. * -or- true if data is available for reading;
    16. * -or- true if the connection has been closed, reset, or terminated;
    17. * otherwise, returns false
    18. */
    19. // Detect if client disconnected
    20. if (connection.Poll(0, SelectMode.SelectRead))
    21. {
    22. byte[] buff = new byte[1];
    23. if (connection.Receive(buff, SocketFlags.Peek) == 0)
    24. {
    25. // Client disconnected
    26. return false;
    27. }
    28. else
    29. {
    30. return true;
    31. }
    32. }
    33. return true;
    34. }
    35. else
    36. {
    37. return false;
    38. }
    39. }
    40. catch
    41. {
    42. return false;
    43. }
    44. }
    45. }



    Was mich nun wundert ist, dass die Exception nicht überall auftritt. Verwende ich die selbe Funktion in Winforms, dann funktioniert alles wie es soll. Selbiges gilt für UWP.

    Lediglich die Android Version der Cross-Plattform App wirft diese Exception.

    Ich Vermute, dass es etwas mit dem IAsyncResult zu tun hat. Habt Ihr vielleicht eine Idee?

    Und wenn ich gerade eh schon mal frage: Wo besteht der Unterschied zwischen .Close() beim Socket und .EndConnect()? Wie setzte ich EndConnect richtig ein, bzw. muss ich das überhaupt verwenden?

    LG Marvin
    .EndConnect() bildet mit .BeginConnect() den Asynchronen aufruf der .Connect() Methode. Dafür übergibst du .BeginConnect() einen AsyncCallback, in dem dann .EndConnect() aufgerufen wird, sobald eine Verbindung besteht. Da du jedoch .WaitOne() des IAsyncResult verwendest, ist dein Verbindungsaufbau Synchron, und blockiert den aktuellen Thread.
    docs.microsoft.com/de-de/dotne…t?view=netframework-4.7.2

    Wenn dir async/await geläufig ist, würde ich dir Raten .ConnectAsync() zu benutzen.
    @EaranMaleasi Jou

    MarvinKleinMusic schrieb:

    Der Aufruf der Connect-Funktion findet dabei im Konstruktor des AufgabenApi Objects statt.
    Mach Dir se separate Boolean-Funktion, die die Verbindung herstellt und die den Erfolg zurückgibt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!