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

У меня есть функция COM, которая должна возвращать SafeArray через LPSAFEARRAY* выходной параметр. Функция создает 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,

3 ответа

Решение

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

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

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

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

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

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

Другие вопросы по тегам