Копиране между VARIANT и _variant_t

Доста съм сигурен, че мога безопасно да направя:

void funcA(VARIANT &V,_variant_t &vt)
{
    vt = V;
}

Но какво ще кажете за обратното:

void funcB(VARIANT &V,_variant_t &vt)
{
    V = vt;
}

Виждам някакво НАИСТИНА странно поведение в приложението си, което отдавам на свързани с COM проблеми с нишката. Но тогава се зачудих дали прецаквам паметта, използвайки варианти погрешно. Във funcB VARIANT V е част от безопасен масив, който се подготвя за COM повикване. С моя ред V=vt, правя ли плитко копие, което ще развали нещата, когато един и същ вариант бъде освободен два пъти?

Наистина харесвам _variant_t и избягвам всички методи ::VariantXXX, има ли добър начин да използвам _variant_t във funcB за автоматизиране на копирането?


person Mr. Boy    schedule 24.11.2009    source източник


Отговори (2)


Първо, да, като използвате оператора за присвояване по начина, по който го правите в funcB(), вие извиквате само плитко копиране (може да искате да погледнете в oaidl.h, за да видите дефиницията VARIANT - той няма дефиниран от потребителя оператор за присвояване и следователно плитко копиране се извършва от компилатора).

Това ви въвежда в недефинирано поведение, ако другият вариант, от който сте копирали, е изчистен, преди да получите достъп до плиткото копие (например, ако типът на варианта е бил VT_UNKNOWN, посоченият обект може просто да бъде унищожен след задаване на броя на препратките на 0 чрез извикване на IUnknown::Release() ).

_variant_t няма да ви помогне много, тъй като няма методи за копиране в друг обект - вижте comutil.h за дефиниция на клас - той копира само от друг обект в себе си.

Най-лесният начин би бил да използвате VariantCopy(). Не съм сигурен дали safearray ще бъде инициализиран, когато се справите с него. Ако се инициализира с всеки елемент, който има VT_EMPTY, можете просто да извикате VariantCopy(). В противен случай първо извикайте VariantInit() на дестинацията, за да инициализирате дестинацията. Извикването на VariantCopy() за дестинация, съдържаща произволни неинициализирани данни, може да доведе до недефинирано поведение.

person sharptooth    schedule 25.11.2009
comment
Вероятно ще получите недефинирано поведение във всеки случай поради двойно изтриване (в случай на BSTR) и висящи указатели (в случай на IUnknown), когато както safearray, така и _varaint_t са унищожени. - person Motti; 26.11.2009
comment
Извикването на VariantClear на неинициализиран VARIANT също ще се повреди. Уверете се, че VARIANT е инициализиран с помощта на VariantInit, преди да опитате да копирате в него. - person Kim Gräsman; 27.11.2009

Ако VARIANT съдържа обект или BSTR, вие ще срещнете проблеми, когато освободите safearray, тъй като освобождаването на safearray ще освободи ресурса, който той не притежава. Така че, когато или _variant_t, или safearray бъдат унищожени, другият ще има препратка към освободен обект.

Например, ако VARIANT съдържа указател към IUnknown, ще объркате броя на препратките, като извикате Release повече пъти от AddRef, ако съдържа BSTR, просто ще копирате указателя и няма да разпределите нов низ за новата променлива.

Ето защо трябва да използвате VariantCopy, ако искате да избегнете Variant* методи (по причина, която не мога да разбера), това може да се направи с _variant_t::Detach()

void funcB(VARIANT &V,_variant_t &vt)
{
    _variant_t temp = vt;
    V = temp.Detach();
    // or in one line V = _variant_t(vt).Detach(); 
}
person Motti    schedule 26.11.2009