Как вернуть локальный CComSafeArray в выходной параметр LPSAFEARRAY?

У меня есть функция COM, которая должна возвращать SafeArray через параметр LPSAFEARRAY* out. Функция создает SafeArray с использованием класса шаблонов ATL CComSafeArray. Моя наивная реализация использует CComSafeArray<T>::Detach(), чтобы переместить право собственности из локальной переменной в выходной параметр:

void foo(LPSAFEARRAY* psa)
{
    CComSafeArray<VARIANT> ret;
    ret.Add(CComVariant(42));
    *psa = ret.Detach();
}

int main()
{
    CComSafeArray<VARIANT> sa;
    foo(sa.GetSafeArrayPtr());

    std::cout << sa[0].lVal << std::endl;
}

Проблема заключается в том, что CComSafeArray::Detach() выполняет операцию Unlock, так что когда новый владелец SafeArray (основной sa в этом случае) уничтожается, блокировка не равна нулю, а Destroy не удается разблокировать SafeArray с E_UNEXPECTED (это приводит к утечке памяти, поскольку SafeArray не освобождается).

Каков правильный способ передачи права собственности на CComSafeArrays через границу метода COM?


Изменить: От одного ответа до сих пор кажется, что ошибка на стороне клиента (main), а не на стороне сервера (foo), но мне трудно полагая, что CComSafeArray не был разработан для этого тривиального случая использования, должен быть элегантный способ получить SafeArray из COM-метода в CComSafeArray.

Ответ 1

Проблема заключается в том, что вы напрямую устанавливаете внутренний указатель CComSafeArray. Используйте метод Attach() для присоединения существующего SAFEARRAY к CComSafeArray:

LPSAFEARRAY ar;
foo(&ar);
CComSafeArray<VARIANT> sa;
sa.Attach(ar);

Ответ 2

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

Выведенная реализация метода неверна, вы не можете предположить, что вызывающий абонент будет предоставлять действительный SAFEARRAY. Просто [out] не является допустимым атрибутом в Automation, он должен быть либо [out, retval], либо [in, out]. Если это [out, retval], как это выглядит, тогда метод должен создать новый массив с нуля. Если это [in, out], тогда метод должен уничтожить переданный массив, если он не соответствует ожидаемому типу массива и создает новый.

Ответ 3

Я бы предположил, что там, где не было намерения допускать такой случай использования. Вероятно, это был не тот самый разработчик, который написал CComVariant и CComPtr:)

Я считаю, что CComSafeArray автор рассматривал семантику ценности как главную цель; Attach/Detach может просто быть "бонусной" функцией.