共用方式為


處理 .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 是的 是的
FileNotFoundException 是的 是的
DirectoryNotFoundException 是的 是的
DriveNotFoundException 是的 是的
PathTooLongException 是的 是的
OperationCanceledException 是的 是的
UnauthorizedAccessException 是的 是的
ArgumentException .NET Core 2.0 和更早版本 是的
NotSupportedException 是的
SecurityException 僅限有限信任

處理 IOException

作為命名空間中例外狀況的 System.IO 基類, IOException 也會針對未對應至預先定義的例外狀況類型的任何錯誤碼擲回 。 這表示它可以由任何輸入/輸出作業拋出。

這很重要

因為 IOException 是命名空間中 System.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 檔案已經存在。
ERROR_INVALID_PARAMETER(錯誤:無效的參數) 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

另請參閱