Dela via


Hantera I/O-fel i .NET

Förutom de undantag som kan utlösas i valfritt metodanrop (till exempel OutOfMemoryException när ett system är stressat eller på grund av programmerarfel NullReferenceException ) kan .NET-filsystemmetoder utlösa följande undantag:

Mappa felkoder till undantag

Eftersom filsystemet är en operativsystemresurs anropar I/O-metoder i både .NET Core- och .NET Framework-omslutningar till det underliggande operativsystemet. När ett I/O-fel inträffar i kod som körs av operativsystemet returnerar operativsystemet felinformation till .NET I/O-metoden. Metoden översätter sedan felinformationen, vanligtvis i form av en felkod, till en .NET-undantagstyp. I de flesta fall gör den detta genom att direkt översätta felkoden till motsvarande undantagstyp. den utför inte någon särskild mappning av felet baserat på kontexten för metodanropet.

I Windows-operativsystemet mappas till exempel ett metodanrop som returnerar en felkod ERROR_FILE_NOT_FOUND för (eller 0x02) till en FileNotFoundException, och en felkod ERROR_PATH_NOT_FOUND för (eller 0x03) mappas till en DirectoryNotFoundException.

De exakta villkor under vilka operativsystemet returnerar specifika felkoder är dock ofta odokumenterade eller dåligt dokumenterade. Därför kan oväntade undantag inträffa. Eftersom du till exempel arbetar med en katalog i stället för en fil förväntar du dig att en ogiltig katalogsökväg till DirectoryInfo konstruktorn genererar en DirectoryNotFoundException. Men det kan också kasta en FileNotFoundException.

Undantagshantering i I/O-åtgärder

På grund av detta beroende av operativsystemet kan identiska undantagsvillkor (till exempel att katalogen inte hittades i vårt exempel) leda till att en I/O-metod genererar någon av hela klassen med I/O-undantag. Det innebär att när du anropar I/O-API:er bör koden vara beredd att hantera de flesta eller alla av dessa undantag, som du ser i följande tabell:

Undantagstyp .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 och tidigare Ja
NotSupportedException No Ja
SecurityException Nej Endast begränsat förtroende

Hantera IOException

Som basklass för undantag i System.IO namnområdet IOException genereras även för all felkod som inte mappas till en fördefinierad undantagstyp. Det innebär att den kan genereras av alla I/O-åtgärder.

Viktigt!

Eftersom IOException är basklassen för de andra undantagstyperna System.IO i namnområdet bör du hantera i ett catch block när du har hanterat de andra I/O-relaterade undantagen.

Från och med .NET Core 2.1 kontrolleras dessutom sökvägens korrekthet (till exempel för att säkerställa att ogiltiga tecken inte finns i en sökväg) och körningen genererar ett undantag som mappas från en operativsystemfelkod i stället för från dess egen valideringskod. Det mest sannolika undantaget som utlöses i det här fallet är en IOException, även om alla andra undantagstyper också kan genereras.

Observera att i koden för undantagshantering bör du alltid hantera den IOException sista. Annars, eftersom det är basklassen för alla andra I/O-undantag, utvärderas inte fångstblocken för härledda klasser.

När det gäller kan IOExceptiondu få ytterligare felinformation från egenskapen IOException.HResult . Om du vill konvertera HResult-värdet till en Win32-felkod tar du bort de övre 16 bitarna av 32-bitarsvärdet. I följande tabell visas felkoder som kan vara omslutna i en IOException.

HResult Konstant beskrivning
ERROR_SHARING_VIOLATION 32 Filnamnet saknas eller så används filen eller katalogen.
ERROR_FILE_EXISTS 80 Filen finns redan.
ERROR_INVALID_PARAMETER 87 Ett argument som anges i metoden är ogiltigt.
ERROR_ALREADY_EXISTS 183 Filen eller katalogen finns redan.

Du kan hantera dessa med hjälp av en When sats i en catch-instruktion, vilket visas i följande exempel.

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

Se även