.NET에서 I/O 오류 처리
메서드 호출에서 throw될 수 있는 예외(예: 시스템 부하가 큰 경우의 NullReferenceException 또는 프로그래머 오류로 인한 OutOfMemoryException) 외에도, .NET 파일 시스템 메서드는 다음과 같은 예외를 throw할 수 있습니다.
- System.IO.IOException - 모든 System.IO 예외 유형의 기본 클래스입니다. 운영 체제의 반환 코드가 다른 예외 유형에 직접 매핑되지 않는 오류에 대해 throw됩니다.
- System.IO.FileNotFoundException.
- System.IO.DirectoryNotFoundException.
- DriveNotFoundException.
- System.IO.PathTooLongException.
- System.OperationCanceledException.
- System.UnauthorizedAccessException.
- System.ArgumentException - .NET Framework와 .NET Core 2.0 및 이전 버전에서 잘못된 경로 문자에 대해 throw됩니다.
- System.NotSupportedException - .NET Framework의 잘못된 콜론에 대해 throw됩니다.
- System.Security.SecurityException - .NET Framework에서만 필요한 권한이 없는, 제한된 신뢰로 실행되는 애플리케이션에 대해 throw됩니다. (.NET Framework의 기본값은 완전 신뢰입니다.)
예외에 오류 코드 매핑
파일 시스템은 운영 체제 리소스이므로 .NET Core와 .NET Framework의 I/O 메서드는 기본 운영 체제 호출을 래핑합니다. 운영 체제가 실행한 코드에서 I/O 오류가 발생할 경우 운영 체제는 .NET I/O 메서드에 오류 정보를 반환합니다. 그런 다음, 메서드가 일반적으로 오류 코드 형태인 오류 정보를 .NET 예외 유형으로 변환합니다. 대부분의 경우 이 작업을 위해 오류 코드를 해당 예외 유형으로 직접 변환하며, 메서드 호출 컨텍스트에 따라 오류의 특수 매핑을 수행하지 않습니다.
예를 들어 Windows 운영 체제에서 ERROR_FILE_NOT_FOUND
의 오류 코드(또는 0x02)를 반환하는 메서드 호출은 FileNotFoundException에 매핑되고, ERROR_PATH_NOT_FOUND
의 오류 코드(또는 0x03)를 반환하는 메서드 호출은 DirectoryNotFoundException에 매핑됩니다.
그러나 운영 체제가 특정 오류 코드를 반환하는 정확한 조건은 문서화되지 않거나 잘못 문서화된 경우가 많습니다. 따라서 예기치 않은 예외가 발생할 수 있습니다. 예를 들어 파일이 아닌 디렉터리로 작업하고 있기 때문에 DirectoryInfo 생성자에 잘못된 디렉터리 경로를 제공하면 DirectoryNotFoundException이 throw됩니다. 그러나 FileNotFoundException이 throw될 수도 있습니다.
I/O 작업의 예외 처리
이러한 운영 체제 사용 때문에 예제의 디렉터리를 찾을 수 없음 오류와 같은 동일한 예외 조건에서 I/O 메서드가 I/O 예외의 전체 클래스 중 하나를 임의로 throw할 수 있습니다. 따라서 I/O API를 호출할 때 다음 표와 같이 이러한 예외를 대부분 또는 모두 처리할 수 있도록 코드를 준비해야 합니다.
예외 종류 | .NET Core/.NET 5 이상 | .NET Framework |
---|---|---|
IOException | 예 | 네 |
FileNotFoundException | 네 | 네 |
DirectoryNotFoundException | 네 | 네 |
DriveNotFoundException | 네 | 네 |
PathTooLongException | 네 | 네 |
OperationCanceledException | 네 | 네 |
UnauthorizedAccessException | 네 | 예 |
ArgumentException | .NET Core 2.0 및 이전 | 예 |
NotSupportedException | 없음 | 네 |
SecurityException | 아니요 | 제한된 신뢰만 |
IOException 처리
System.IO 네임스페이스에 있는 예외의 기본 클래스인 IOException은 미리 정의된 예외 유형에 매핑되지 않는 오류 코드에 대해서도 throw됩니다. 따라서 이 예외는 모든 I/O 작업에서 throw될 수 있습니다.
Important
IOException은 System.IO 네임스페이스에 있는 다른 예외 유형의 기본 클래스이므로 다른 I/O 관련 예외를 처리한 후에 catch
블록에서 처리해야 합니다.
또한 .NET Core 2.1부터 경로 정확성에 대한 유효성 검사(예: 경로에 잘못된 문자가 없는지 확인)가 제거되었으며, 런타임 시 자체 유효성 검사 코드가 아닌 운영 체제 오류 코드에서 매핑된 예외가 throw됩니다. 이 경우에 throw될 가능성이 큰 예외는 IOException이지만 다른 예외 유형도 throw될 수 있습니다.
예외 처리 코드에서 IOException을 항상 마지막에 처리해야 합니다. 그러지 않으면 다른 모든 IO 예외의 기본 클래스이므로 파생 클래스의 catch 블록이 평가되지 않습니다.
IOException의 경우 IOException.HResult 속성에서 추가 오류 정보를 얻을 수 있습니다. HResult 값을 Win32 오류 코드로 변환하려면 32비트 값의 상위 16비트를 제거합니다. 다음 표에는 IOException에 래핑될 수 있는 오류 코드가 나와 있습니다.
HResult | 상수 | 설명 |
---|---|---|
ERROR_SHARING_VIOLATION | 32 | 파일 이름이 없거나 파일 또는 디렉터리가 사용 중입니다. |
ERROR_FILE_EXISTS | 80 | 파일이 이미 있습니다. |
ERROR_INVALID_PARAMETER | 87 | 메서드에 제공된 인수가 잘못되었습니다. |
ERROR_ALREADY_EXISTS | 183 | 파일 또는 디렉터리가 이미 있습니다. |
다음 예제와 같이 catch 문에서 When
절을 사용하여 이러한 예외를 처리할 수 있습니다.
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
참고 항목
.NET