Gestion des erreurs E/S dans .NET

Outre les exceptions qui peuvent être levées dans n’importe quel appel de méthode (comme OutOfMemoryException lorsqu’un système est sous contrainte ou NullReferenceException en raison d’erreurs de programmation), les méthodes du système de fichiers .NET peuvent lever les exceptions suivantes :

Mappage des codes d'erreur aux exceptions

Étant donné que le système de fichiers est une ressource de système d’exploitation, les méthodes E/S dans .NET Core et .NET Framework encapsulent les appels avec le système d’exploitation sous-jacent. Lorsqu’une erreur E/S se produit dans le code exécuté par le système d’exploitation, celui-ci retourne des informations sur l’erreur à la méthode E/S .NET. La méthode convertit ensuite les informations sur l’erreur, généralement sous la forme d’un code d’erreur, dans un type d’exception .NET. Dans la plupart des cas, cela signifie traduire directement le code d’erreur dans son type d’exception correspondant. La méthode n’effectue pas un mappage spécial de l’erreur en fonction du contexte de l’appel de la méthode.

Par exemple, sur le système d’exploitation Windows, un appel de méthode qui retourne un code d’erreur ERROR_FILE_NOT_FOUND (ou 0x02) est mappé à FileNotFoundException et le code d’erreur ERROR_PATH_NOT_FOUND (ou 0x03) correspond à DirectoryNotFoundException.

Toutefois, les conditions précises sous lesquelles le système d’exploitation retourne des codes d’erreur particuliers sont souvent non documentées ou mal documentées. Par conséquent, des exceptions inattendues peuvent se produire. Par exemple, étant donné que vous travaillez avec un répertoire au lieu d’un fichier, vous pourriez penser que le fait de fournir un chemin d’accès de répertoire non valide au constructeur DirectoryInfo lèverait une exception DirectoryNotFoundException. Toutefois, une exception FileNotFoundException peut également être levée.

Gestion des exceptions dans les opérations E/S

En raison de cette dépendance envers le système d’exploitation, des conditions d’exception identiques (par exemple, l’erreur répertoire introuvable dans ce cas) peuvent faire qu’une méthode E/S lève l’une des exceptions de la classe entière d’exceptions E/S. Cela signifie que, lors de l’appel d’API E/S, votre code doit être prêt à gérer la plupart ou toutes ces exceptions, comme indiqué dans le tableau suivant :

Type d'exception .NET Core/.NET 5+ .NET Framework
IOException Oui Oui
FileNotFoundException Oui Oui
DirectoryNotFoundException Oui Oui
DriveNotFoundException Oui Oui
PathTooLongException Oui Oui
OperationCanceledException Oui Oui
UnauthorizedAccessException Oui Oui
ArgumentException .NET Core 2.0 et versions antérieures Oui
NotSupportedException No Oui
SecurityException Non Confiance limitée uniquement

Gestion d’IOException

Comme la classe de base pour les exceptions dans l’espace de noms System.IO, IOException est également levée pour n’importe quel code d’erreur qui n’est pas mappé à un type d’exception prédéfini. Cela signifie qu’elle peut être levée par n’importe quelle opération E/S.

Important

Étant donné que IOException est la classe de base des autres types d’exception dans l’espace de noms System.IO, vous devez la gérer dans un bloc catch une fois que vous avez géré les autres exceptions E/S associées.

En outre, à compter de .NET Core 2.1, les contrôles de validation de l’exactitude du chemin d’accès (par exemple, pour s’assurer que des caractères non valides ne sont pas présents dans un chemin d’accès) ont été supprimés, et le runtime lève une exception mappée à partir d’un code d’erreur de système d’exploitation plutôt qu’à partir de son propre code de validation. L’exception la plus susceptible d’être levée dans ce cas est IOException, bien que n’importe quel autre type d’exception puisse également être levé.

Notez que, dans votre code gestion des exceptions, vous devez toujours gérer IOException en dernier. Sinon, étant donné qu’il s’agit de la classe de base de toutes les autres exceptions d’E/S, les blocs catch des classes dérivées ne seront pas évalués.

Dans le cas de IOException, vous pouvez obtenir des informations supplémentaires sur l’erreur à partir de la propriété IOException.HResult. Pour convertir la valeur HResult en un code d’erreur Win32, éliminez les 16 bits supérieurs de la valeur de 32 bits. Le tableau suivant répertorie les codes d’erreur qui peuvent être encapsulés dans IOException.

HResult Constant Description
ERROR_SHARING_VIOLATION 32 Le nom de fichier est manquant ou le fichier ou le répertoire est en cours d’utilisation.
ERROR_FILE_EXISTS 80 Le fichier existe déjà.
ERROR_INVALID_PARAMETER 87 Un argument fourni à la méthode n’est pas valide.
ERROR_ALREADY_EXISTS 183 Le fichier ou le répertoire existe déjà.

Vous pouvez les gérer à l’aide d’une clause When dans une instruction catch, comme le montre l’exemple suivant.

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

Voir aussi