Недавно я писал много кода, который включает взаимодействие с API Win32 и начал задаваться вопросом, как лучше всего справляться с собственными (неуправляемыми) ошибками, вызванными вызовами функций Windows API.
В настоящее время вызовы функций native выглядят примерно так:
// 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, который был установлен, а также (как правило) понятное для человека описание ошибки в качестве свойства Message
объекта Exception. Тем не менее, я думал, что было бы целесообразно модифицировать/обернуть по крайней мере некоторые, если не все, из этих исключений, чтобы они дали немного более контекстно-ориентированное сообщение об ошибке, то есть еще один смысл в любой ситуации, когда нативный код быть использованным. Я рассмотрел несколько альтернатив для этого:
-
Указание специального сообщения об ошибке в конструкторе для
Win32Exception
.throw new Win32Exception(Marshal.GetLastWin32Error(), "My custom error message.");
-
Обтекание
Win32Exception
в другом объекте Exception, чтобы сохранить и исходный код ошибки и сообщение (Win32Exception
теперьInnerException
родительского исключения).throw new Exception("My custom error message.", Win32Exception(Marshal.GetLastWin32Error()));
-
То же, что и 2, за исключением использования другого
Win32Exception
в качестве исключения оболочки. -
То же, что и 2, за исключением использования пользовательского класса, полученного из
Exception
в качестве исключения оболочки. -
То же, что и 2, за исключением использования исключения BCL (Base Class Library) в качестве родителя при его утверждении. Не уверен, что он даже подходит для установки
InnerException
вWin32Exception
в этом случае (возможно, для низкоуровневой оболочки, но не для более высокого уровня/абстрагированного интерфейса, что не делает очевидным, что взаимодействие Win32 происходит позади сцены?)
По сути, я хочу знать, какова рекомендуемая практика устранения ошибок Win32 в .NET? Я вижу, что это делается с открытым исходным кодом разными способами, но мне было любопытно, существуют ли какие-либо рекомендации по дизайну. Если нет, я буду заинтересован в ваших личных предпочтениях здесь. (Возможно, вы даже не используете ни один из вышеперечисленных методов?)