Partager via


Gestion des erreurs d’E/S dans .NET

Outre les exceptions pouvant être levées dans n’importe quel appel de méthode (par OutOfMemoryException exemple, lorsqu’un système est stressé ou en raison d’une NullReferenceException erreur de programmeur), les méthodes de 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 d’E/S dans .NET Core et .NET Framework encapsulent les appels au système d’exploitation sous-jacent. Lorsqu’une erreur d’E/S se produit dans le code exécuté par le système d’exploitation, le système d’exploitation retourne des informations d’erreur à la méthode d’E/S .NET. La méthode traduit ensuite les informations d’erreur, généralement sous la forme d’un code d’erreur, en type d’exception .NET. Dans la plupart des cas, elle effectue cette opération en convertissant directement le code d’erreur en son type d’exception correspondant ; elle n’effectue aucun mappage spécial de l’erreur en fonction du contexte de l’appel de 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é à un FileNotFoundException, et un code d’erreur de ERROR_PATH_NOT_FOUND (ou 0x03) est mappé à un DirectoryNotFoundException.

Toutefois, les conditions précises dans 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 plutôt qu’un fichier, vous vous attendez à ce que fournir un chemin de répertoire non valide au constructeur DirectoryInfo provoque une exception DirectoryNotFoundException. Toutefois, une exception FileNotFoundException peut également être levée.

Gestion des exceptions dans les opérations d’E/S

En raison de cette dépendance sur le système d’exploitation, des conditions d’exception identiques (telles que l’erreur de répertoire introuvable dans notre exemple) peuvent entraîner une méthode d’E/S qui lève l’une des classes entières d’exceptions d’E/S. Cela signifie que, lors de l’appel d’API d’E/S, votre code doit être prêt à gérer la plupart ou l’ensemble de 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 Non Oui
SecurityException Non Confiance limitée uniquement

Gestion d’IOException

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

Importante

Parce que IOException est la classe de base des autres types d'exceptions dans l'espace de noms System.IO, vous devriez le gérer dans un bloc catch une fois que vous avez traité les autres exceptions liées aux E/S.

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

Notez que, dans votre code de gestion des exceptions, vous devez toujours gérer le IOException 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 d’un IOException, vous pouvez obtenir des informations d’erreur supplémentaires à partir de la propriété IOException.HResult . Pour convertir la valeur HResult en code d’erreur Win32, vous supprimez les 16 bits supérieurs de la valeur 32 bits. Le tableau suivant répertorie les codes d’erreur qui peuvent être encapsulés dans un IOException.

HResult Constante Descriptif
ERREUR_DE_VIOLATION_DE_PARTAGE 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à.
ERREUR_PARAMÈTRE_INVALIDE 87 Un argument fourni à la méthode n’est pas valide.
ERREUR_DEJA_EXISTE 183 Le fichier ou le répertoire existe déjà.

Vous pouvez les gérer à l’aide d’une When clause dans une instruction catch, comme l’illustre 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