DllImport — атрибуты PreserverSig и SetLastError

В 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?

С уважением ПК


person pkolodziej    schedule 18.04.2009    source источник


Ответы (1)


Функции 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. Управляемое объявление вашей функции DllImported имеет тип возвращаемого значения void.

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

person Anton Tykhyy    schedule 18.04.2009
comment
У меня нет опыта работы с COM-объектами, поэтому позвольте мне задать еще один вопрос о создании сигнатуры метода. Вопрос в следующем: когда я вижу, что функция COM возвращает HRESULT, я могу пометить свой метод как возвращающий void и установить PreserveSig=false (как вы сказали) или установить PreserveSig=true и пометить мой метод как возвращающий IntPtr, чтобы вручную проверить возвращенный код? - person pkolodziej; 18.04.2009
comment
Да, это правильно, за исключением того, что HRESULT — это UInt32, а не IntPtr. - person Anton Tykhyy; 18.04.2009
comment
Спасибо - вы очень помогли. - person pkolodziej; 19.04.2009