抛出 Win32Exception

发布于 2024-07-15 07:52:38 字数 1984 浏览 8 评论 0原文

我最近编写了大量涉及与 Win32 API 互操作的代码,并且开始想知道处理由调用 Windows API 函数引起的本机(非托管)错误的最佳方法是什么。

目前,对本机函数的调用看起来像这样:

// 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 错误代码的异常作为(通常)人类可读的错误描述,作为 Exception 对象的 Message 属性。 但是,我一直在认为,建议至少修改/包装这些异常中的一些(如果不是全部),以便它们给出稍微更面向上下文的错误消息,即在本机代码的任何情况下都更有意义正在使用。 我已经考虑了几种替代方案:

  1. Win32Exception 的构造函数中指定自定义错误消息。
throw new Win32Exception(Marshal.GetLastWin32Error(), "My custom error message.");
  1. Win32Exception 包装在另一个 Exception 对象中,以便保留原始错误代码和消息(Win32Exception 现在是父异常的 InnerException )。
throw new Exception("My custom error message.",
        Win32Exception(Marshal.GetLastWin32Error()));
  1. 与 2 相同,只是使用另一个 Win32Exception 作为包装异常。

  2. 与 2 相同,只是使用从 Exception 派生的自定义类作为包装异常。

  3. 与 2 相同,只是在适当的时候使用 BCL(基类库)异常作为父异常。 不确定在这种情况下将 InnerException 设置为 Win32Exception 是否合适(可能适用于低级包装器,但不适用于不支持的高级/抽象接口)难道Win32互操作是在幕后发生的吗?)

本质上我想知道的是:处理 .NET 中的 Win32 错误的推荐做法是什么? 我看到它以各种不同的方式在开源代码中完成,但我很好奇是否有任何设计指南。 如果没有,我会对您的个人喜好感兴趣。 (或许以上方法你都没有使用过?)

I've been writing a lot of code recently that involves interop with the Win32 API and have been starting to wonder what's the best way to deal with native (unmanaged) errors that are caused by calls to Windows API functions.

Currently, the calls to native functions look something like this:

// 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();

The line that raises the exception can be equivalently rewritten as such I believe:

throw new Win32Exception(Marshal.GetLastWin32Error());

Now, this is all well in that it throws an exception appropriately containing the Win32 error code that was set as well as a (generally) human-readable description of the error as the Message property of the Exception object. However, I have been thinking that it would be advisable to modify/wrap at least some, if not all, of these exceptions so that they give a slightly more context-oriented error message, i.e. one more meaningful in whatever situation the native code is being used. I have considered several alternatives for this:

  1. Specifying a custom error message in the constructor for Win32Exception.
throw new Win32Exception(Marshal.GetLastWin32Error(), "My custom error message.");
  1. Wrapping the Win32Exception in another Exception object so that both the original error code and message are retained (the Win32Exception is now the InnerException of the parent exception).
throw new Exception("My custom error message.",
        Win32Exception(Marshal.GetLastWin32Error()));
  1. The same as 2, except using another Win32Exception as the wrapper exception.

  2. The same as 2, except using a custom class derived from Exception as the wrapper exception.

  3. The same as 2, except using a BCL (Base Class Library) exception as the parent when appropiate. Not sure whether it's even appropiate to set the InnerException to the Win32Exception in this case (perhaps for a low-level wrapper but not a higher-level/abstracted interface that doesn't make it obvious that Win32 interop is happening behind the scenes?)

Essentially what I want to know is: what is the recommended practice on dealing with Win32 errors in .NET? I see it done in open-source code in all sorts of different ways, but I was curious whether there were any design guidelines. If not, I'd be interested in your personal preferences here. (Perhaps you even use none of the above methods?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

魄砕の薆 2024-07-22 07:52:38

这并不是真正特定于 Win32 异常; 问题是,什么时候应该由两个不同的 Exception 派生类型来标识两个不同的错误情况,以及它们什么时候应该抛出内部存储有不同值的相同类型?

不幸的是,如果不事先知道代码将被调用的所有情况,这是不可能回答的。:) 这是只能按类型过滤异常的问题。 一般来说,如果您强烈认为以不同的方式处理两个错误情况会很有用,请抛出不同的类型。

否则,经常出现的情况是 Exception.Message 返回的字符串只需要记录或显示给用户。

如果有其他信息,请用您自己的更高级别的内容包装 Win32Exception。 例如,您正在尝试对文件执行某些操作,而您运行的用户没有执行此操作的权限。 捕获 Win32Exception,将其包装在您自己的异常类中,其消息给出文件名和正在尝试的操作,后跟内部异常的消息。

This isn't really specific to Win32 exceptions; the question is, when should two different error cases be identified by two different Exception-derived types, and when should they throw the same type with different values stored inside it?

Unfortunately this is impossible to answer without knowing in advance all the situations your code will be called in. :) This is the problem with only being able to filter exceptions by type. Broadly speaking, if you have a strong feeling that it would be useful to treat two error cases differently, throw different types.

Otherwise, it is frequently the case that the string returned by Exception.Message just needs to be logged or displayed to the user.

If there is additional information, wrap the Win32Exception with something more high-level of your own. For example, you're trying to do something to a file, and the user you're running under doesn't have permission to do it. Catch the Win32Exception, wrap it in an exception class of your own, whose message gives the filename and the operation being attempted, followed by the inner exception's message.

南薇 2024-07-22 07:52:38

我的观点始终是,处理此问题的适当方法取决于目标受众以及您的课程将如何使用。

如果您的类/方法的调用者意识到他们正在以一种或另一种形式调用 Win32,我将使用您指定的选项 1)。 这对我来说似乎是最“清楚”的。 (但是,如果是这种情况,我会以明确将直接使用 Win32 API 的方式命名您的类)。 话虽这么说,为了更清楚,BCL 中的一些异常实际上是 Win32Exception 的子类,而不是仅仅包装它。 例如,SocketException 派生自 Win32Exception。 我个人从未使用过这种方法,但它似乎确实是处理此问题的一种潜在的干净方法。

如果类的调用者不知道您直接调用 Win32 API,我将处理该异常,并使用您定义的自定义的、更具描述性的异常。 例如,如果我正在使用您的类,并且没有迹象表明您正在使用 Win32 api(因为您出于某些特定的、非显而易见的原因在内部使用它),那么我没有理由怀疑我可能需要处理 Win32Exception。 您始终可以记录这一点,但对我来说,捕获它并给出一个在您的特定业务环境中更有意义的例外似乎更合理。 在这种情况下,我可能会将初始的 Win32Exception 包装为内部异常(即:您的情况 4),但根据导致内部异常的原因,我可能不会。

此外,很多时候本机调用会引发 Win32Exception,但 BCL 中还有其他更相关的异常。 当您调用未包装的本机 API,但 BCL 中包装了类似的函数时,就会出现这种情况。 在这种情况下,我可能会捕获该异常,确保它是我所期望的,然后在其位置抛出标准的 BCL 异常。 一个很好的例子是使用 SecurityException 而不是抛出 Win32Exception。

但总的来说,我会避免您列出的选项 2 和 3。

选项二抛出一般异常类型 - 我强烈建议完全避免这种情况。 将特定的异常包装成更通用的异常似乎是不合理的。

选项三似乎多余——与选项一相比确实没有优势。

My view has always been that the appropriate way to handle this depends on the target audience and how your class is going to be used.

If the caller of your class/method is going to realize that they are calling into Win32 in one form or another, I would use option 1) you have specified. This seems the most "clear" to me. (However, if this is the case, I'd name your class in a manner that makes it clear that the Win32 API is going to be used directly). That being said, there are exceptions in the BCL that actually subclass Win32Exception to be more clear, instead of just wrapping it. For example, SocketException derives from Win32Exception. I've never personally used that approach, but it does seem like a potentially clean way to handle this.

If the caller of your class is going to have no idea that you're calling into the Win32 API directly, I would handle the exception, and use a custom, more descriptive exception you define. For example, if I'm using your class, and there's no indication that you're using the Win32 api (since you're using it internally for some specific, non-obvious reason), I would have no reason to suspect that I may need to handle a Win32Exception. You could always document this, but it seems more reasonable to me to trap it and give an exception that would have more meaning in your specific business context. In this case, I might wrap the initial Win32Exception as an inner exception (ie: your case 4), but depending on what caused the internal exception, I might not.

Also, there are many times when a Win32Exception would be thrown from a native call, but there are other exceptions in the BCL that are more relevant. This is the case when you're calling into a native API that isn't wrapped, but there are similar functions that ARE wrapped in the BCL. In that case, I would probably trap the exception, make sure that it is what I'm expecting, but then throw the standard, BCL exception in its place. A good example of this would be to use SecurityException instead of throwing a Win32Exception.

In general, though, I would avoid option 2 and 3 you listed.

Option two throws a general Exception type - I would pretty much recommend avoiding that entirely. It seems unreasonable to wrap a specific exception into a more generalized one.

Option three seems redundant - There's really no advantage over Option 1.

洛阳烟雨空心柳 2024-07-22 07:52:38

就我个人而言,我会做#2或#4......最好是#4。 将 Win32Exception 包装在上下文相关的异常中。 像这样:

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

这样,如果有人捕获异常,他们就会很清楚问题发生在哪里。 我不会做#1,因为它需要捕获来解释您的文本错误消息。 #3 并没有真正提供任何额外的信息。

Personally I would do #2 or #4...Preferably #4. Wrap Win32Exception inside your exception which is context sensitive. Like this:

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

This way if somebody catches the exception, they will have a pretty good idea where the problem occurred. I wouldn't do #1 because it requires the catch to interpret your text error message. And #3 doesn't really give any additional information.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文