DllImport - атрибуты PreserverSig и SetLastError - PullRequest
9 голосов
/ 18 апреля 2009

В MSDN я нашел следующее описание для двух атрибутов:

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

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

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

Кроме того, зачем мне вызывать метод GetLastWin32Error, если мне каким-то образом удалось извлечь исключение с помощью PreserveSig?

С уважением PK

1 Ответ

14 голосов
/ 18 апреля 2009

Функции 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 ed в качестве типа возврата будет void.

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

...