共用方式為


在 .NET 中處理 I/O 錯誤

除了在任何方法呼叫中擲回的例外狀況以外 (例如系統負荷過高造成的 OutOfMemoryException,或程式設計人員出錯造成的 NullReferenceException),.NET 檔案系統方法還會擲回下列例外狀況:

將錯誤碼對應至例外狀況

因為檔案系統是作業系統資源,所以 .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 Yes .是
FileNotFoundException .是 .是
DirectoryNotFoundException .是 .是
DriveNotFoundException .是 .是
PathTooLongException .是 .是
OperationCanceledException .是 .是
UnauthorizedAccessException .是 Yes
ArgumentException .NET Core 2.0 及更早版本
NotSupportedException .是
SecurityException No 僅限有限信任

處理 IOException

作為 System.IO 命名空間中例外狀況的基底類別,IOException 也會因為任何錯誤碼未對應至預先定義的例外狀況類型而擲回。 這表示此例外狀況可由任何 I/O 作業擲回。

重要

因為 IOExceptionSystem.IO 命名空間中其他例外狀況類型的基底類別,所以您應該在處理過其他 I/O 相關例外狀況之後,在 catch 區塊中處理此例外狀況。

此外,從 .NET Core 2.1 開始,已移除路徑正確性的驗證檢查 (例如,為了確保無效字元不會出現在路徑中),而且執行階段會從作業系統錯誤碼而非它自己的驗證碼,擲回對應的例外狀況。 在此情況下最有可能擲回的例外狀況是 IOException,但是也可能會擲回其他任何例外狀況類型。

請注意,在您的例外狀況處理程式碼中,您應該一律最後處理 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

另請參閱