CComVariant преминава като VARIANT

Искам да извикам функция на трета страна от моя код. Прототипът на функцията е:

HRESULT foo(/*[in]*/VARIANT, /*[in]*/VARIANT*, /*[out]*/VARIANT*)

В момента използвам CComVariant обвивки около моите VARIANT променливи и искам да ги предам на тази функция. Малко съм объркан, как да го направя. В случай на входните аргументи, трябва ли просто да ги предам директно или да ги отделя в обикновен VARIANT и да предам тези променливи? Както виждам и двете версии работят, но кой е по-чистият и препоръчителен метод? Текущият ми код е нещо подобно:

CComVariant param0(CComBSTR( "Hello world"));
CComVariant param1(VARIANT_FALSE);
CComVariant param2;
HRESULT hr = foo(param0, &param1, &param2);

person Dexterslab    schedule 15.04.2016    source източник
comment
/*[in]*/ VARIANT* -- наистина ли?   -  person Roger Lipscombe    schedule 15.04.2016
comment
Да, не е печатна грешка. :) И аз не го разбирам, но това е друга история.   -  person Dexterslab    schedule 15.04.2016
comment
Кодът е наред (не е необходимо отделяне на конкретна стойност преди или след повикване; фрагментът прави всичко). Защо? Това е друга история, но е добре.   -  person Roman R.    schedule 15.04.2016
comment
Само за да се уверя, че го разбирам правилно: когато предам аргумента по стойност, обектът се нарязва, така че се копира само частта VARIANT и докато функцията само я чете, няма да причини никакъв проблем. След като функцията се върне, ресурсите ще бъдат освободени в края на блока, нали? В случай на out аргумент: аргументът е преминал през базовия указател, функцията записва в частта VARIANT (вече е инициализирана от конструктора на CComVariant), след като функцията се върне, ресурсите се освобождават от деструктора, когато променливата излезе извън обхвата .   -  person Dexterslab    schedule 16.04.2016
comment
Горе-долу правилно. когато [в] аргумент е, както казвате, нарязан и след това изпълнението се връща от повикването - няма нищо за освобождаване, оригиналът на нарязаното копие е собственост на някого и той ще освободи ресурсите по нормален начин, несвързан с това нарязване . С [out] аргумент предоставяте контейнер, който да бъде попълнен. Вие носите отговорност за безплатните данни. В този пример вие предоставяте заместител като CComVariant екземпляр, който автоматично освобождава всичко, което е записано в него, когато променливата излезе извън обхвата. Тоест, вие сте този, който освобождава ресурси и го правите с помощта на този помощен клас.   -  person Roman R.    schedule 17.04.2016
comment
Нищо не е нарязано, CComVariant не добавя никакви допълнителни полета или виртуални методи към структурата VARIANT.   -  person Hans Passant    schedule 18.04.2016
comment
Благодаря за помощта момчета!   -  person Dexterslab    schedule 20.04.2016


Отговори (1)


Да, този код е наред и едва ли може да се направи по-добре, като се има предвид как е проектиран ATL::CComVariant, освен ако не отделите известно време за писане на помощни функции. Няколко мисли за това.

Предаването на CComVariant като in VARIANT е добре - основната част просто се копира по член в параметъра на функцията и се използва от извиканата функция оттам. Не се случва дълбоко копиране и това е добре, извикваният може да използва копието също така, ако иска да копира дълбоко параметъра или AddRef() него - той все още може да го направи.

Предаването на адрес на CComVariant като in VARIANT е добре (CComVariant* е имплицитно upcast към VARIANT*, функцията има достъп до подобекта), но не е най-добрият код в света. Ето защо. Има много подобни класове, които притежават ресурси, най-важните от тях са ATL::CComBSTR, _bstr_t, ATL::CComPtr, _com_ptr_t и всички те са operator&() претоварени, а също и тези от тях, които принадлежат към ATL пространството от имена, просто връщат указателя към съхранения указател, но _bstr_t и _com_ptr_t освобождаване на притежавания обект преди връщане на указателя. Ето защо това:

ATL::CComPtr<IUnknown> ptr( initialize() );
&ptr;

и този:

_com_ptr_t<IUnknown> ptr( initialize() );
&ptr;

имат различни ефекти. Нито ATL::CComVariant, нито _variant_t са operator&() претоварени, което усложнява нещата още малко. По-чистият начин би бил да се използва метод за "извличане на достъп", като _variant_t::GetVARIANT(), но ATL::CComVariant няма такъв метод. Така че използването на & е единствената опция, която имате тук.

Предаването на CComVariant адрес, получен с помощта на & като out VARIANT* е добре, но отново не е най-добрият код в света. Добре е, когато знаете със сигурност, че вариантът е празен, но този код:

CComVariant var( obtainIUnknown() );
foo( whatever, whatever, &var );

сляпо ще презапише указателя и ще доведе до изтичане на по-рано споменатия обект. По-добър начин би бил да се използва нещо като _variant_t::GetAddress(), което изчиства варианта и след това връща указател към необработения VARIANT, но отново ATL::CComVariant няма такъв метод.

Така че долният код е добре, просто внимавайте, когато го променяте. Ако се случи да пишете много код като този, може да е по-добре да пишете помощни функции, които извършват тези манипулации за "извличане".

person sharptooth    schedule 18.04.2016
comment
Благодаря за подробното обяснение! Никога преди не съм използвал COM, така че беше наистина полезно! - person Dexterslab; 20.04.2016
comment
@sharptooth бихте ли ми помогнали с този въпрос, който публикувах тук stackoverflow.com/questions/42295574/ - person Joseph; 23.02.2017