Behandeln von E/A-Fehlern in .NETHandling I/O errors in .NET

Über die Ausnahmen hinaus, die bei jedem Methodenaufruf ausgelöst werden können (wie etwa eine OutOfMemoryException, wenn ein System unter Stress steht, oder eine NullReferenceException aufgrund eines Programmierfehlers), können Methoden des .NET-Dateisystems die folgenden Ausnahmen auslösen:In addition to the exceptions that can be thrown in any method call (such as an OutOfMemoryException when a system is stressed or an NullReferenceException due to programmer error), .NET file system methods can throw the following exceptions:

Zuordnen von Fehlercodes zu AusnahmenMapping error codes to exceptions

Da das Dateisystem eine Betriebssystemressource ist, umschließen E/A-Methoden sowohl in .NET Core als auch in .NET Framework Aufrufe an das zugrundeliegende Betriebssystem.Because the file system is an operating system resource, I/O methods in both .NET Core and .NET Framework wrap calls to the underlying operating system. Wenn in Code, der vom Betriebssystem ausgeführt wird, ein E/A-Fehler auftritt, gibt das Betriebssystem Fehlerinformationen zu der .NET-E/A-Methode zurück.When an I/O error occurs in code executed by the operating system, the operating system returns error information to the .NET I/O method. Die Methode übersetzt dann die Fehlerinformationen, normalerweise in Form eines Fehlercodes, in einen .NET-Ausnahmetyp.The method then translates the error information, typically in the form of an error code, into a .NET exception type. In den meisten Fällen erfolgt dies durch direktes Übersetzen des Fehlercodes in den entsprechenden Ausnahmetyp; es wird keine besondere Zuordnung des Fehlers auf der Grundlage des Kontexts des Methodenaufrufs vorgenommen.In most cases, it does this by directly translating the error code into its corresponding exception type; it does not perform any special mapping of the error based on the context of the method call.

Beispielsweise wird im Windows-Betriebssystem ein Methodenaufruf, der den Fehlercode ERROR_FILE_NOT_FOUND (oder 0x02) zurückgibt, einer FileNotFoundException zugeordnet, und der Fehlercode ERROR_PATH_NOT_FOUND (oder 0x03) wird einer DirectoryNotFoundException zugeordnet.For example, on the Windows operating system, a method call that returns an error code of ERROR_FILE_NOT_FOUND (or 0x02) maps to a FileNotFoundException, and an error code of ERROR_PATH_NOT_FOUND (or 0x03) maps to a DirectoryNotFoundException.

Die genauen Umstände, unter denen das Betriebssystem bestimmte Fehlercodes zurückgibt, sind aber häufig undokumentiert oder schlecht dokumentiert.However, the precise conditions under which the operating system returns particular error codes is often undocumented or poorly documented. Daher können unerwartete Ausnahmen auftreten.As a result, unexpected exceptions can occur. Wenn Sie beispielsweise mit einem Verzeichnis anstelle einer Datei arbeiten, könnten Sie annehmen, dass die Angabe eines falschen Verzeichnispfads für den DirectoryInfo.DirectoryInfo-Konstruktor eine DirectoryNotFoundException auslöst.For example, because you are working with a directory rather than a file, you would expect that providing an invalid directory path to the DirectoryInfo.DirectoryInfo constructor throws a DirectoryNotFoundException. Es kann aber ebenso gut eine FileNotFoundException auslösen.However, it may also throw a FileNotFoundException.

Ausnahmebehandlung in E/A-VorgängenException handling in I/O operations

Aufgrund dieses Rückgriffs auf das Betriebssystem können identische Ausnahmebedingungen (wie etwa der Fehler des nicht gefundenen Verzeichnisses in unserem Beispiel) dazu führen, dass eine E/A-Methode irgendeine aus der gesamten Klasse der E/A-Ausnahmen auslöst.Because of this reliance on the operating system, identical exception conditions (such as the directory not found error in our example) can result in an I/O method throwing any one of the entire class of I/O exceptions. Das bedeutet, dass beim Aufrufen von E/A-APIs Ihr Code in der Lage sein sollte, die meisten oder alle diese Ausnahmen zu behandeln, wie in der folgenden Tabelle dargestellt:This means that, when calling I/O APIs, your code should be prepared to handle most or all of these exceptions, as shown in the following table:

AusnahmetypException type .NET Core.NET Core .NET Framework.NET Framework
IOException JaYes JaYes
FileNotFoundException JaYes JaYes
DirectoryNotFoundException JaYes JaYes
DriveNotFoundException JaYes JaYes
PathTooLongException JaYes JaYes
OperationCanceledException JaYes JaYes
UnauthorizedAccessException JaYes JaYes
ArgumentException .NET Core 2.0 und früher.NET Core 2.0 and earlier JaYes
NotSupportedException NeinNo JaYes
SecurityException NeinNo Eingeschränkte VertrauenswürdigkeitLimited trust only

Behandlung von IOExceptionHandling IOException

Als Basisklasse für Ausnahmen im Namespace System.IO wird IOException auch für jeden Fehlercode ausgelöst, der sich keinem vordefinierten Ausnahmetyp zuordnen lässt.As the base class for exceptions in the System.IO namespace, IOException is also thrown for any error code that does not map to a predefined exception type. Das bedeutet, dass sie von jedem E/A-Vorgang ausgelöst werden kann.This means that it can be thrown by any I/O operation.

Wichtig

Da IOException die Basisklasse der anderen Ausnahmetypen im Namespace System.IO ist, sollten Sie sie nach der Behandlung der anderen Ausnahmen im E/A-Zusammenhang in einem catch-Block behandeln.Because IOException is the base class of the other exception types in the System.IO namespace, you should handle in a catch block after you've handled the other I/O-related exceptions.

Seit .NET Core 2.1 wurden darüber hinaus die Gültigkeitsprüfungen von Pfaden (um beispielsweise sicherzustellen, dass keine ungültigen Zeichen in einem Pfad vorhanden sind) entfernt, und die Runtime löst eine Ausnahme aus, die von einem Fehlercode des Betriebssystems anstelle seines eigenen Validierungscodes zugeordnet wurde.In addition, starting with .NET Core 2.1, validation checks for path correctness (for example, to ensure that invalid characters are not present in a path) have been removed, and the runtime throws an exception mapped from an operating system error code rather than from its own validation code. In diesem Fall ist die Ausnahme, die mit der größten Wahrscheinlichkeit ausgelöst wird, IOException; allerdings könnte auch jeder andere Ausnahmetyp ausgelöst werden.The most likely exception to be thrown in this case is an IOException, although any other exception type could also be thrown.

Beachten Sie, dass Sie in Ihrem Fehlerbehandlungscode die IOException immer zuletzt behandeln sollten.Note that, in your exception handling code, you should always handle the IOException last. Da sie die Basisklasse aller anderen EA-Ausnahmen bildet, werden sonst die Catch-Blöcke von abgeleiteten Klassen nicht ausgewertet.Otherwise, because it is the base class of all other IO exceptions, the catch blocks of derived classes will not be evaluated.

Im Fall einer IOException können Sie zusätzliche Fehlerinformationen aus der Eigenschaft IOException.HResult abrufen.In the case of an IOException, you can get additional error information from the IOException.HResult property. Um den HResult-Wert in einen Win32-Fehlercode zu konvertieren, entfernen Sie die oberen 16 Bits des 32-Bit-Werts.To convert the HResult value to a Win32 error code, you strip out the upper 16 bits of the 32-bit value. In der folgenden Tabelle sind Fehlercodes aufgelistet, die von einer IOException umschlossen werden können.The following table lists error codes that may be wrapped in an IOException.

HResultHResult KonstanteConstant BeschreibungDescription
ERROR_SHARING_VIOLATIONERROR_SHARING_VIOLATION 3232 Der Dateiname fehlt, oder die Datei oder das Verzeichnis wird verwendet.The file name is missing, or the file or directory is in use.
ERROR_FILE_EXISTSERROR_FILE_EXISTS 8080 Die Datei ist bereits vorhanden.The file already exists.
ERROR_INVALID_PARAMETERERROR_INVALID_PARAMETER 8787 Ein der Methode übergebenes Argument ist ungültig.An argument supplied to the method is invalid.
ERROR_ALREADY_EXISTSERROR_ALREADY_EXISTS 183183 Die Datei oder das Verzeichnis ist bereits vorhanden.The file or directory already exists.

Sie können diese mithilfe einer When-Klausel in einer catch-Anweisung behandeln, wie im folgenden Beispiel dargestellt.You can handle these using a When clause in a catch statement, as the following example shows.

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 maxium 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 maxium 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:\nError code: " +
                              $"{e.HResult And &h0000FFFF}\nMessage: {e.Message}")
        End Try
        Return Nothing
    End Function
End Module

Siehe auchSee also