Правильный доступ к SafeArray из VT_UNKNOWN с SafeArrayGetElement

StackOverflow https://stackoverflow.com/questions/1609025

  •  05-07-2019
  •  | 
  •  

Вопрос

У нас есть COM-компонент, который & # 8217; реализация и определение интерфейса существуют в управляемом коде, но управляются собственным компонентом. Управляемый компонент возвращает SafeArray обратно в собственный код с помощью следующего объявления метода.

interface IExample {
  <return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UNKNOWN)>
  object[] DoSomeOperation()
}

Сгенерированная собственная подпись правильно передает это обратно как IUnknown.

Во время проверки кода мы задали несколько вопросов о вызовах результирующего массива с помощью SafeArrayGetElement. Вопрос в том, возвращает ли SafeArrayGetElement <=> экземпляр, который является AddRef'd или нет. По сути это сводится к тому, что из следующего является правильным

Пример 1:

CComPtr<IUnknown> spUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&spUnk));

Пример 2:

IUnknown* pUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&pUnk));

Документация на эту тему очень тонкая. Он содержит только следующую строку.

  

Если элемент данных представляет собой строку, объект или вариант, функция копирует элемент правильным образом.

Определение правильности немного неоднозначно.

Это было полезно?

Решение

Первый метод должен быть правильным и должен соответствовать обработке объектов в COM, вероятно, найденное вами определение предполагает, что потребитель знает правильный путь.

Другие упомянутые элементы требуют этого. Копирование VARIANT или SAFEARRAY содержит неявный AddRef (), когда они содержат объекты. VARIANT не требует его, когда присутствует VT_BYREF.

VariantCopy @ MSDN
SafeArrayCopy @ MSDN

Это поведение не присуще SAFEARRAY или VARIANT, поскольку оно является частью правил обработки параметров в COM. Однако ничто не мешает кому-то пытаться обойти правила.

Для входных параметров вызывающий не несет ответственности перед AddRef (), если только он не намеревается сохранить указатель интерфейса для дальнейшего использования. Однако другие случаи использования параметров требуют этого.

Например, для интерфейсов, помещенных в VARIANT или другие контейнеры, должен применяться хотя бы один вызов AddRef (), в противном случае это может создать проблемы при использовании VARIANT в качестве выходных параметров из методов COM, поскольку передача данных / ссылок является односторонней. Срок действия исходного объекта может истечь к тому времени, когда вызов прибудет к месту назначения. Точно так же маршалинг интерфейса в Stream также требует AddRef ().

Аналогично, для вызова по ссылке требуется, по крайней мере, один вызов AddRef. Если это не так, то любой подходящий длительный вызов (скажем, через DCOM) может не прийти к месту назначения с гарантией того, что указанный объект все еще жив. Однако здесь часто пропускаются дополнительные вызовы AddRef () / Release (), поскольку объект уже должен быть на уровне 1+ из-за создания в или перед областью вызова.

Если есть возможность изменить компонент, и ваши вызовы не обрабатываются, то вместо этого может быть желательно использовать GIT. Это позволяет вам вместо этого передавать токен, и будет проще маршалировать интерфейс через COM-квартиры. Время жизни задействованных объектов становится обязанностью вызывающей стороны в течение всего времени вызова, и вы сможете отслеживать случаи, когда объект не может быть маршалирован.

Создание таблицы глобального интерфейса @ MSDN

Также интересна сноска для BSTR.

  

Если реализация функции, которая принимает эталонный параметр BSTR, назначает этому параметру новый BSTR, она должна освободить ранее указанный BSTR.

Функции управления строками (COM)

Другие советы

Это должен быть AddRef: ed, но у меня нет информации из первых рук, что это так (например, я не читал источник).

Я думаю, что документация довольно ясна - копировать указатель интерфейса «правильно» - это AddRef: ing.

Если вы хотите быть действительно уверенным, создайте сверхпростой COM-объект ATL, который реализует IUnknown, поместите несколько из них в SAFEARRAY и установите точку останова в CComObjectBase<>::InternalAddRef (если мне не изменяет память). Затем отладьте вызов SafeArrayGetElement и проверьте, достигнута ли ваша точка останова.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top