Я пытаюсь разобраться в беспорядках определений интерфейса 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);
Есть ли какая-либо функциональная разница между этими тремя? Кто-нибудь из них считается "лучше", чем другие по причинам, кроме технической корректности?