次の方法で共有


.NET での I/O エラーの処理

.NET ファイル システム メソッドは、任意のメソッド呼び出しでスローできる例外 (システムがストレスを受けた場合の OutOfMemoryException やプログラマ エラーによる NullReferenceException など) に加えて、次の例外をスローできます。

エラー コードを例外にマッピングする

ファイル システムはオペレーティング システム リソースであるため、.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がスローされます。 ただし、 FileNotFoundExceptionをスローする場合もあります。

I/O 操作での例外処理

このオペレーティング システムへの依存により、同じ例外条件 (この例ではディレクトリが見つからないエラーなど) により、I/O メソッドが I/O 例外のクラス全体のいずれかをスローする可能性があります。 つまり、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がスローされます。 これは、任意の I/O 操作によってスローできることを意味します。

Von Bedeutung

IOExceptionSystem.IO名前空間の他の例外型の基底クラスであるため、他の I/O 関連の例外を処理した後、catch ブロックで処理する必要があります。

さらに、.NET Core 2.1 以降では、パスの正確性の検証チェック (たとえば、パスに無効な文字が存在しないことを確認するため) が削除され、ランタイムは、独自の検証コードではなく、オペレーティング システムのエラー コードからマップされた例外をスローします。 この場合にスローされる可能性が最も高い例外は IOExceptionですが、他の例外の種類もスローされる可能性があります。

例外処理コードでは、常に最後の IOException を処理する必要があることに注意してください。 それ以外の場合は、他のすべての IO 例外の基底クラスであるため、派生クラスの catch ブロックは評価されません。

IOExceptionの場合は、IOException.HResult プロパティから追加のエラー情報を取得できます。 HResult 値を Win32 エラー コードに変換するには、32 ビット値の上位 16 ビットを除去します。 次の表に、 IOExceptionでラップされる可能性があるエラー コードを示します。

HResult 定数 説明
共有違反エラー 32 ファイル名がないか、ファイルまたはディレクトリが使用中です。
ERROR_FILE_EXISTS 80 ファイルは既に存在します。
無効なパラメーターエラー 87 メソッドに指定された引数が無効です。
すでに存在するエラー 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

こちらも参照ください