Delen via


I/O-fouten verwerken in .NET

Naast de uitzonderingen die kunnen optreden in een methodeaanroep (zoals wanneer OutOfMemoryException een systeem onder druk staat of door een programmeurfout NullReferenceException), kunnen .NET-bestandssysteemmethoden de volgende uitzonderingen veroorzaken:

Foutcodes toewijzen aan uitzonderingen

Omdat het bestandssysteem een besturingssysteemresource is, verpakken I/O-methoden in zowel .NET Core als .NET Framework aanroepen naar het onderliggende besturingssysteem. Wanneer een I/O-fout optreedt in code die door het besturingssysteem wordt uitgevoerd, retourneert het besturingssysteem foutinformatie naar de .NET I/O-methode. De methode vertaalt vervolgens de foutgegevens, meestal in de vorm van een foutcode, in een .NET-uitzonderingstype. In de meeste gevallen gebeurt dit door de foutcode rechtstreeks te vertalen in het bijbehorende uitzonderingstype; er wordt geen speciale toewijzing van de fout uitgevoerd op basis van de context van de methode-aanroep.

Op het Windows-besturingssysteem wordt bijvoorbeeld een methodeaanroep geretourneerd waarmee een foutcode van ERROR_FILE_NOT_FOUND (of 0x02) wordt toegewezen aan een FileNotFoundException, en een foutcode van ERROR_PATH_NOT_FOUND (of 0x03) wordt toegewezen aan een DirectoryNotFoundException.

De precieze omstandigheden waaronder het besturingssysteem bepaalde foutcodes retourneert, zijn echter vaak niet-gedocumenteerd of slecht gedocumenteerd. Als gevolg hiervan kunnen onverwachte uitzonderingen optreden. Omdat u bijvoorbeeld met een map werkt in plaats van een bestand, zou u verwachten dat het doorgeven van een ongeldig mappad aan de DirectoryInfo constructor een DirectoryNotFoundException genereert. Het kan ook echter een FileNotFoundException gooien.

Afhandeling van uitzonderingen in I/O-bewerkingen

Vanwege deze afhankelijkheid van het besturingssysteem kunnen identieke uitzonderingsvoorwaarden (zoals de fout in het voorbeeld van de map niet gevonden) resulteren in een I/O-methode die een van de hele klasse I/O-uitzonderingen genereert. Dit betekent dat bij het aanroepen van I/O-API's uw code moet worden voorbereid om de meeste of al deze uitzonderingen af te handelen, zoals wordt weergegeven in de volgende tabel:

Uitzonderingstype .NET Core/.NET 5+ .NET Framework
IOException Ja Ja
FileNotFoundException Ja Ja
DirectoryNotFoundException Ja Ja
DriveNotFoundException Ja Ja
PathTooLongException Ja Ja
OperationCanceledException Ja Ja
UnauthorizedAccessException Ja Ja
ArgumentException .NET Core 2.0 en eerder Ja
NotSupportedException Nee. Ja
SecurityException Nee. Alleen beperkte vertrouwensrelatie

IOException verwerken

Als de basisklasse voor uitzonderingen in de System.IO naamruimte, wordt IOException ook gegooid voor foutcodes die niet aan een vooraf gedefinieerd uitzonderingsstype worden toegewezen. Dit betekent dat het kan worden opgeworpen door elke I/O-bewerking.

Belangrijk

Omdat IOException dit de basisklasse is van de andere uitzonderingstypen in de System.IO naamruimte, moet u in een catch blok verwerken nadat u de andere I/O-gerelateerde uitzonderingen hebt verwerkt.

Vanaf .NET Core 2.1 zijn de controles voor de correctheid van paden (bijvoorbeeld om te garanderen dat er geen ongeldige tekens in een pad staan) verwijderd. De runtime gooit nu een uitzondering gebaseerd op een foutcode van het besturingssysteem in plaats van de eigen validatiecode. De meest waarschijnlijke uitzondering die in dit geval wordt gegenereerd, is een IOException, hoewel elk ander uitzonderingstype ook kan worden gegenereerd.

Houd er rekening mee dat u in uw uitzonderingsverwerkingscode altijd de IOException laatste moet afhandelen. Anders worden de catchblokken van afgeleide klassen niet geƫvalueerd, omdat dit de basisklasse is van alle andere IO-uitzonderingen.

In het geval van een IOException, kunt u aanvullende foutinformatie ophalen uit de eigenschap IOException.HResult . Als u de HResult-waarde wilt converteren naar een Win32-foutcode, wordt de bovenste 16 bits van de 32-bits waarde verwijderd. De volgende tabel bevat foutcodes die kunnen worden verpakt in een IOException.

HResult Constante Beschrijving
ERROR_DEELCOVOORTREDING 32 De bestandsnaam ontbreekt of het bestand of de map wordt gebruikt.
ERROR_FILE_EXISTS 80 Het bestand bestaat al.
FOUT_ONJUISTE_PARAMETER 87 Een argument dat aan de methode is opgegeven, is ongeldig.
ERROR_ALREADY_EXISTS 183 Het bestand of de map bestaat al.

U kunt deze verwerken met behulp van een When component in een catch-instructie, zoals in het volgende voorbeeld wordt weergegeven.

using System;
using System.IO;
using System.Text;

class Program
{
    static void Main()
    {
        var sw = OpenStream(@".\textfile.txt");
        if (sw is null)
            return;
        sw.WriteLine("This is the first line.");
        sw.WriteLine("This is the second line.");
        sw.Close();
    }

    static StreamWriter? OpenStream(string path)
    {
        if (path is null)
        {
            Console.WriteLine("You did not supply a file path.");
            return null;
        }

        try
        {
            var fs = new FileStream(path, FileMode.CreateNew);
            return new StreamWriter(fs);
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("The file or directory cannot be found.");
        }
        catch (DirectoryNotFoundException)
        {
            Console.WriteLine("The file or directory cannot be found.");
        }
        catch (DriveNotFoundException)
        {
            Console.WriteLine("The drive specified in 'path' is invalid.");
        }
        catch (PathTooLongException)
        {
            Console.WriteLine("'path' exceeds the maximum supported path length.");
        }
        catch (UnauthorizedAccessException)
        {
            Console.WriteLine("You do not have permission to create this file.");
        }
        catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32)
        {
            Console.WriteLine("There is a sharing violation.");
        }
        catch (IOException e) when ((e.HResult & 0x0000FFFF) == 80)
        {
            Console.WriteLine("The file already exists.");
        }
        catch (IOException e)
        {
            Console.WriteLine($"An exception occurred:\nError code: " +
                              $"{e.HResult & 0x0000FFFF}\nMessage: {e.Message}");
        }
        return null;
    }
}
Imports System.IO

Module Program
    Sub Main(args As String())
        Dim sw = OpenStream(".\textfile.txt")
        If sw Is Nothing Then Return

        sw.WriteLine("This is the first line.")
        sw.WriteLine("This is the second line.")
        sw.Close()
    End Sub

    Function OpenStream(path As String) As StreamWriter
        If path Is Nothing Then
            Console.WriteLine("You did not supply a file path.")
            Return Nothing
        End If

        Try
            Dim fs As New FileStream(path, FileMode.CreateNew)
            Return New StreamWriter(fs)
        Catch e As FileNotFoundException
            Console.WriteLine("The file or directory cannot be found.")
        Catch e As DirectoryNotFoundException
            Console.WriteLine("The file or directory cannot be found.")
        Catch e As DriveNotFoundException
            Console.WriteLine("The drive specified in 'path' is invalid.")
        Catch e As PathTooLongException
            Console.WriteLine("'path' exceeds the maximum supported path length.")
        Catch e As UnauthorizedAccessException
            Console.WriteLine("You do not have permission to create this file.")
        Catch e As IOException When (e.HResult And &h0000FFFF) = 32
            Console.WriteLine("There is a sharing violation.")
        Catch e As IOException When (e.HResult And &h0000FFFF) = 80
            Console.WriteLine("The file already exists.")
        Catch e As IOException
            Console.WriteLine($"An exception occurred:{vbCrLf}Error code: " +
                              $"{e.HResult And &h0000FFFF}{vbCrLf}Message: {e.Message}")
        End Try
        Return Nothing
    End Function
End Module

Zie ook