2009-04-18 4 views
9

На MSDN я нашел следующее описание для двух атрибутов:DllImport - PreserverSig и SetLastError атрибуты

PreserveSig Установите поле PreserveSig к верному непосредственно перевести неуправляемые подписи с HRESULT или RetVal значений; установите для него значение false, чтобы автоматически преобразовывать значения HRESULT или retval в исключения. По умолчанию поле PreserveSig имеет значение true.

SetLastError Позволяет вызывающему абоненту использовать функцию API Marshal.GetLastWin32Error для определения того, произошла ли ошибка при выполнении метода. В Visual Basic значение по умолчанию истинно (что добавляет некоторые издержки); в C# и C++ значение по умолчанию - false.

Мой вопрос: как эти два относятся друг к другу? Предположим, что у меня установлен PreserveSig на «false» - это означает, что я должен был преобразовать HRESULT в исключение - если неуправляемая функция возвращает целое число, указывающее на ошибку или отсутствие ошибки, как это можно перевести на исключение?

Также почему мне нужно вызвать метод GetLastWin32Error, если мне как-то удалось извлечь исключение, используя PreserveSig?

Сердечные приветы PK

ответ

14

функции Win32 почти никогда не возвращают HRESULT. Вместо этого они возвращают BOOL или используют специальные значения для указания ошибки (например, CreateFile возвращает INVALID_HANDLE_VALUE). Они сохраняют код ошибки в переменной для каждой строки, которую вы можете читать с помощью GetLastError(). SetLastError=true инструктирует маршалера прочитать эту переменную после возвращения нативной функции и спрятать код ошибки, где позже вы можете прочитать его с помощью Marshal.GetLastWin32Error(). Идея состоит в том, что среда выполнения .NET может вызывать другие функции Win32 за кулисами, которые испортили код ошибки из вашего вызова p/invoke, прежде чем вы сможете проверить его.

Функции, которые возвращают HRESULT (или эквивалент, например NTSTATUS), относятся к другому уровню абстракции, чем функции Win32. Обычно эти функции связаны с COM (выше Win32) или от ntdll (ниже Win32), поэтому они не используют код последней ошибки Win32 (хотя они могут вызывать функции Win32 внутри).

PreserveSig=false инструктирует маршалинг проверить возвращение HRESULT и если это не код успеха, чтобы создать и бросить исключение, содержащее HRESULT. Управляемая декларация вашей функции DllImport затем имеет void в качестве возвращаемого типа.

Помните, что компилятор C# или VB не может проверить неуправляемую подпись функции DllImport ed, поэтому она должна доверять тому, что вы ей скажете. Если вы положили PreserveSig=false на функцию, которая возвращает что-то, отличное от HRESULT, вы получите странные результаты (например, случайные исключения). Если вы положили SetLastError=true на функцию, которая не устанавливает последний код ошибки Win32, вы получите мусор вместо полезного кода ошибки.

+0

У меня нет опыта работы с объектами COM, поэтому позвольте мне задать еще один вопрос о создании сигнатуры метода. Вопрос: когда я вижу, что функция COM возвращает HRESULT, я могу пометить мой метод как возвращающий void и установить PreserveSig = false (как вы сказали) или установить PreserveSig = true и пометить мой метод как возвращающий IntPtr для ручного изучения возвращаемого кода? – pkolodziej

+0

Да, это правильно, за исключением того, что HRESULT - это UInt32s, а не IntPtrs. –

+0

Спасибо, вы были очень полезны. – pkolodziej