簡體   English   中英

拋出 Win32Exception

[英]Throwing a Win32Exception

我最近一直在編寫很多涉及與 Win32 API 互操作的代碼,並且開始想知道處理由調用 Windows ZDB974238714CA8DE634A7CED1 函數引起的本機(非托管)錯誤的最佳方法是什么。

目前,對本機函數的調用如下所示:

// NativeFunction returns true when successful and false when an error
// occurred. When an error occurs, the MSDN docs usually tell you that the
// error code can be discovered by calling GetLastError (as long as the
// SetLastError flag has been set in the DllImport attribute).
// Marshal.GetLastWin32Error is the equivalent managed function, it seems.
if (!WinApi.NativeFunction(param1, param2, param3))
    throw new Win32Exception();

引發異常的行可以等效地重寫,我相信:

throw new Win32Exception(Marshal.GetLastWin32Error());

現在,這一切都很好,因為它會引發一個異常,其中包含設置的 Win32 錯誤代碼以及(通常)人類可讀的錯誤描述,作為異常 object 的Message屬性。 但是,我一直認為建議至少修改/包裝這些異常中的一些(如果不是全部),以便它們提供稍微更面向上下文的錯誤消息,即在本機代碼的任何情況下更有意義的錯誤消息是正在使用。 為此,我考慮了幾種替代方案:

  1. Win32Exception的構造函數中指定自定義錯誤消息。

     throw new Win32Exception(Marshal.GetLastWin32Error(), "My custom error message.");
  2. Win32Exception包裝在另一個異常 object 中,以便保留原始錯誤代碼和消息( Win32Exception現在是父異常的InnerException )。

     throw new Exception("My custom error message.", Win32Exception(Marshal.GetLastWin32Error()));
  3. 與 2 相同,但使用另一個Win32Exception作為包裝異常。

  4. 與 2 相同,但使用從Exception派生的自定義 class 作為包裝異常。

  5. 與 2 相同,除了在適當時使用 BCL(Base Class 庫)異常作為父項。 不確定在這種情況下將InnerException設置為Win32Exception是否合適(可能是低級包裝器,但不是更高級別/抽象的接口,這不會使 Win32 互操作在幕后發生明顯?)

基本上我想知道的是:在 .NET 中處理 Win32 錯誤的推薦做法是什么? 我看到它以各種不同的方式在開源代碼中完成,但我很好奇是否有任何設計指南。 如果沒有,我會在這里對您的個人喜好感興趣。 (也許您甚至沒有使用上述方法?)

這並不是 Win32 異常所特有的。 問題是,兩種不同的異常派生類型何時應該識別兩種不同的錯誤情況,何時應該拋出存儲在其中的不同值的相同類型?

不幸的是,如果不事先知道將調用您的代碼的所有情況,這是不可能回答的。:) 這是只能按類型過濾異常的問題。 從廣義上講,如果您強烈認為以不同的方式處理兩種錯誤情況會很有用,請拋出不同的類型。

否則,通常情況下 Exception.Message 返回的字符串只需要記錄或顯示給用戶。

如果有其他信息,請使用您自己的更高級別的內容包裝 Win32Exception。 例如,您正在嘗試對文件執行某些操作,而您正在運行的用戶沒有執行此操作的權限。 捕獲 Win32Exception,將其包裝在您自己的異常 class 中,其消息給出文件名和正在嘗試的操作,然后是內部異常的消息。

我的觀點一直是,處理此問題的適當方法取決於目標受眾以及您的 class 將如何使用。

如果您的類/方法的調用者將意識到他們正在以一種或另一種形式調用 Win32,我將使用您指定的選項 1)。 這對我來說似乎是最“清楚”的。 (但是,如果是這種情況,我會將您的 class 命名為清楚地表明將直接使用 Win32 API)。 話雖如此,BCL 中有一些異常實際上是 Win32Exception 的子類,以便更清楚地了解,而不僅僅是包裝它。 例如,SocketException 派生自 Win32Exception。 我從來沒有親自使用過這種方法,但它似乎確實是一種潛在的干凈方式來處理這個問題。

如果您的 class 的調用者不知道您正在直接調用 Win32 API,我將處理該異常,並使用您定義的自定義、更具描述性的異常。 例如,如果我正在使用您的 class,並且沒有跡象表明您正在使用 Win32 api(因為您出於某些特定的、非顯而易見的原因在內部使用它),我沒有理由懷疑我可能需要處理 Win32Exception。 您總是可以記錄這一點,但對我來說,將其捕獲並給出一個在您的特定業務環境中具有更多意義的異常似乎更合理。 在這種情況下,我可能會將初始 Win32Exception 包裝為內部異常(即:您的情況 4),但取決於導致內部異常的原因,我可能不會。

此外,很多時候會從本機調用中引發 Win32Exception,但 BCL 中還有其他更相關的異常。 當您調用未包裝的本機 API 時就是這種情況,但也有類似的函數被包裝在 BCL 中。 在這種情況下,我可能會捕獲異常,確保它是我所期望的,然后拋出標准的 BCL 異常。 一個很好的例子是使用 SecurityException 而不是拋出 Win32Exception。

不過,總的來說,我會避免您列出的選項 2 和 3。

選項二會拋出一個通用的異常類型——我非常建議完全避免這種情況。 將特定異常包裝成更通用的異常似乎是不合理的。

選項 3 似乎是多余的 - 與選項 1 相比確實沒有優勢。

就我個人而言,我會做#2 或#4 ...最好是#4。 將 Win32Exception 包裝在上下文相關的異常中。 像這樣:

void ReadFile()
{
    if (!WinApi.NativeFunction(param1, param2, param3))
        throw MyReadFileException("Couldn't read file", new Win32Exception());
}

這樣,如果有人捕捉到異常,他們就會很清楚問題發生在哪里。 我不會做#1,因為它需要捕獲來解釋您的文本錯誤消息。 而#3 並沒有真正提供任何額外的信息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM