Когда необходимо/целесообразно использовать InAttribute и OutAttribute для COM Interop

Я пытаюсь разобраться в беспорядках определений интерфейса COM, которые мы разбросали по различным проектам, и собрать их в единое, хорошо известное место, от которого может выиграть вся команда разработчиков. Часть этих усилий связана с очисткой определений, которые были накоплены за эти годы.

Некоторые из них заимствованы из другого исходного кода, некоторые из них были скопированы дословно из pinvoke.net, и некоторые из них выглядят напрямую переведенными из заголовков SDK. Одна вещь, которую я замечаю, заключается в том, что нет никакой последовательности в том, когда использовать различные атрибуты маршаллинга (даже среди примеров pinvoke.net это довольно удачный или промах). Частично проблема заключается в том, что я не думаю, что кто-либо здесь (включая меня) полностью понимает, когда нужны или нет какие-либо атрибуты или что они на самом деле делают. До этого момента получение этих прав кажется комбинацией догадок и случайных изменений до тех пор, пока не прекратятся COMExceptions, но я бы предпочел, чтобы переводы были правильными, потому что кто-то действительно посмотрел на них и объявил им это.

Итак, я начинаю с [In] и [Out]. Я знаю, что эти два атрибута делают концептуально: они сообщают маршаллеру, в каком направлении должны идти данные. Я предполагаю, что, например, маршаллер не захочет копировать данные [In] обратно вызывающему абоненту или знает, что данные [Out] могут быть освобождены на стороне вызываемого абонента и т.д. То, что я не знаю, это:

  • Когда необходимо использовать эти атрибуты? То есть, когда поведение маршаллинга по умолчанию неправильное, так что атрибуты делают его правильным?
  • Когда безопасно использовать эти атрибуты? То есть укажет, что должно быть поведением по умолчанию, как работает маршаллер?
  • Когда опасно использовать эти атрибуты? Я предполагаю, что явная маркировка выходного параметра [In] плохая, но маркирует входной параметр [In, Out] на самом деле собирается что-то сломать?

Итак, учитывая гипотетический метод COM-интерфейса, IDL которого выглядит так:

HRESULT Foo(
    [in] ULONG a,
    [out] ULONG * b
    [in, out] ULONG * c);

Я мог бы это перевести как любое из следующего:

void Foo(
  uint cb,
  out uint b,
  ref uint c);

void Foo(
  uint cb,
  [Out] out uint b,
  [In, Out] ref uint c);

void Foo(
  [In] uint cb,
  [Out] out uint b,
  [In, Out] ref uint c);

Есть ли какая-либо функциональная разница между этими тремя? Кто-нибудь из них считается "лучше", чем другие по причинам, кроме технической корректности?

Ответ 1

Нет, не обязательно применять какие-либо из этих атрибутов в вашем собственном коде. Они все необязательны и автоматически применяются к вашему коду в зависимости от типа параметра.

По существу, они имеют следующие эквиваленты ключевых слов С#:

  • По умолчанию вы получаете [In].
  • Ключевое слово ref получает вас [In, Out].
  • Ключевое слово out получает вас [Out].

Все задокументированы в таблице здесь.

Вам нужно использовать их только в том случае, если вы хотите изменить эти подразумеваемые семантики или изменить поведение маршалера по умолчанию. Например, вы можете пометить параметр, объявленный ref атрибутом [In] только для подавления "out" части маршалинга.

Тем не менее, я нахожу себя в использовании, потому что это очень простой способ сделать самообъявление кода.