Неявные переводы сигнатур метода в взаимодействиях .NET
Чтобы оставаться не зависящим от языка программирования, система COM Windows и многие API Windows возвращают 4 байтов целочисленного типа, вызываемого для HRESULT
указания успешности или сбоя API, а также некоторые сведения о сбое. Другие значения, которые необходимо передать вызывающему объекту, "возвращаются" с помощью параметров указателя, которые действуют как "out" и обычно являются последним параметром в сигнатуре. Языки, такие как C# и Visual Basic, традиционно переводят код сбоя в исключение, чтобы соответствовать тому, как ошибки обычно распространяются на языке, и ожидают, что подписи методов взаимодействия не включаются HRESULT
. Чтобы преобразовать сигнатуру метода в собственную сигнатуру, среда выполнения перемещает HRESULT
возвращаемое значение метода в дополнительный параметр out с еще одним уровнем косвенного обращения (другими словами, делает его указателем на тип возвращаемой подписи) и предполагает возвращаемое значение. Если управляемый метод возвращается void
, дополнительный параметр не добавляется, а возвращаемое значение становится HRESULT
. Например, см. следующие два метода COM C#, которые преобразуют в ту же собственную сигнатуру:
int Add(int a, int b);
void Add(int a, int b, out int sum);
HRESULT Add(int a, int b, /* out */ int* sum);
СохранитьSig в COM
Все методы COM в C#, как ожидается, будут использовать преобразованную подпись по умолчанию. Чтобы использовать и экспортировать методы без перевода подписей и обработки значений HRESULT
, добавьте PreserveSigAttribute его в метод COM-интерфейса. Если атрибут применяется к методу, преобразование не выполняется в сигнатуру, а исключения не создаются для неудачных HRESULT
значений. Это относится как к встроенному COM, так и к созданному источником COM. Например, см. следующую сигнатуру метода C# с атрибутом PreserveSig
и соответствующей собственной сигнатурой.
[PreserveSig]
int Add(int a, int b, out int sum);
HRESULT Add(int a, int b, int* sum);
Это может быть полезно, если метод может возвращать разные HRESULT
значения, которые не являются сбоями, но должны обрабатываться по-другому. Например, некоторые методы могут возвращать значение S_FALSE
, если метод не завершается ошибкой, но возвращает только частичные результаты, а S_OK
когда возвращает все результаты.
PreserveSig
с помощью P/Invokes
Атрибут DllImportAttribute также имеет bool PreserveSig
поле, которое работает аналогично значению PreserveSigAttribute
, но по умолчанию true
. Чтобы указать, что среда выполнения должна преобразовывать управляемую подпись и обрабатывать HRESULT
возвращаемую, задайте PreserveSig
поле false
в поле DllImportAttribute
. Например, см. следующие сигнатуры двух P/Invokes в одном и том же собственном методе, один с PreserveSig
заданным значением false
и один из них слева от значения по умолчанию true
.
[DllImport("shlwapi.dll", EntryPoint = "SHAutoComplete", ExactSpelling = true, PreserveSig = false)]
public static extern void SHAutoComplete(IntPtr hwndEdit, SHAutoCompleteFlags dwFlags);
[DllImport("shlwapi.dll", EntryPoint = "SHAutoComplete", ExactSpelling = true)]
public static extern int SHAutoCompleteHRESULT(IntPtr hwndEdit, SHAutoCompleteFlags dwFlags);
Примечание.
Созданные источником P/Invokes, которые используют LibraryImportAttributeполе, не PreserveSig
имеют поля. Созданный код всегда предполагает, что собственная и управляемая сигнатура идентичны. Дополнительные сведения см. в разделе "Источник" для P/Invokes.
Обработка значений вручную HRESULT
При вызове PreserveSig
метода, возвращающего объект HRESULT
, можно использовать ThrowExceptionForHR метод для вызова соответствующего исключения, если HRESULT
указывает на сбой. Аналогичным образом при реализации PreserveSig
метода можно использовать GetHRForException метод для возврата HRESULT
соответствующего значения исключения.
Маршал HRESULTs в виде структур
При использовании метода, как ожидается, int
будет управляемым типом PreserveSig
для HRESULT
. Однако использование пользовательской структуры 4-байтов в качестве возвращаемого типа позволяет определять вспомогательные методы и свойства, которые могут упростить работу с HRESULT
ним. В встроенном маршаллинге это работает автоматически. Чтобы использовать структуру вместо int
управляемого HRESULT
представления в исходном маршаллинге, добавьте MarshalAsAttribute атрибут в Error качестве аргумента. Присутствие этого атрибута переосмыслеет биты HRESULT
структуры.