Недавно я написал много кода, включающего взаимодействие с 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, а также (обычно) удобочитаемое описание ошибки в виде свойства 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 (библиотеки базовых классов) в качестве родителя, когда это уместно. Не уверен, что в этом случае даже уместно установить
InnerException
наWin32Exception
(возможно, для низкоуровневой оболочки, но не для высокоуровневого/абстрактного интерфейса, который не делает очевидным, что взаимодействие Win32 происходит за кулисами?)
По сути, я хочу знать: какова рекомендуемая практика работы с ошибками Win32 в .NET? Я видел, как это делается в открытом исходном коде самыми разными способами, но мне было любопытно, существуют ли какие-либо рекомендации по дизайну. Если нет, то мне были бы интересны ваши личные предпочтения здесь. (Возможно, вы даже не используете ни один из вышеперечисленных методов?)