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 :
- System.IO.IOException, la classe de base de tous les types d’exceptions System.IO. Elle est levée pour les erreurs dont les codes d’erreur à partir du système d’exploitation ne sont pas directement mappés vers un autre type d’exception.
- System.IO.FileNotFoundException.
- System.IO.DirectoryNotFoundException.
- DriveNotFoundException.
- System.IO.PathTooLongException.
- System.OperationCanceledException.
- System.UnauthorizedAccessException.
- System.ArgumentException, qui est levée pour les caractères de chemin non valides sur le .NET Framework et .NET Core 2.0 et les versions précédentes.
- System.NotSupportedException, qui est levée pour les deux-points non valides dans le .NET Framework.
- System.Security.SecurityException, qui est levée pour les applications exécutées en mode de confiance limitée et qui ne disposent pas des autorisations nécessaires sur le .NET Framework uniquement. (La confiance totale est la valeur par défaut sur le .NET Framework.)
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