views:

504

answers:

2

I'm fairly certain that I can safely do:

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

But what about the other way around:

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

I've been seeing some REALLY weird behaviour in my app which I put down to COM-related threading issues. But then I got wondering if I was screwing up memory using variants wrongly. In funcB, the VARIANT V is part of a safe-array being prepared for a COM call. With my V=vt line, am I doing a shallow copy which will break things when the same variant gets deallocated twice?

I really like _variant_t and avoiding all the ::VariantXXX methods, is there a neat way to use _variant_t in funcB to automate the copying?

+2  A: 

First of all, yes, by using the assignment operator the way you do in funcB() you invoke shallow copying only (you might want to look into oaidl.h to see the VARIANT definition - it has no user-defined assignment operator and therefore shallow copying is done by the compiler).

This gets you into undefined behaviour if the other variant you copied from is cleared before you access the shallow copy (for example, if the variant type was VT_UNKNOWN the object pointed to could simply be destroyed after setting the reference count to 0 by calling the IUnknown::Release()).

_variant_t will not help you much since it has no methods for copying to another object - see comutil.h for class definition - it only copies from another object to itself.

The easiest way would be to use VariantCopy(). Not sure if the safearray will be initialized when you deal with it. If it is initialized with each element having VT_EMPTY you can just call VariantCopy(). Otherwise first call VariantInit() on the destination to initialize the destination. Calling VariantCopy() for a destination containing random uninitialized data can lead to undefined behaviour.

sharptooth
Great answer, thanks.
John
You'll probably get undefined behaviour in any case due to double deletion (in case of `BSTR`) and dangling pointers (in case of `IUnknown`) when both the safearray and the `_varaint_t` are destroyed.
Motti
Calling VariantClear on an uninitialized VARIANT will also break. Make sure the VARIANT is initialized using VariantInit before attempting to copy to it.
Kim Gräsman
Yes, very true. Fixed.
sharptooth
+2  A: 

If the VARIANT contains an object or a BSTR you will run into trouble when you deallocate the safearray since safearray deallocation will free the resource which it doesn't own. So when either the _variant_t or the safearray is destroyed the other will have a reference to a deallocated object.

For example if the VARIANT contains a pointer to IUnknown you'll mess up the reference count by calling Release more times than AddRef, if it contains a BSTR you'll just copy the pointer and not allocate a new string for the new variable.

This is why you should use VariantCopy, if you want to avoid Variant* methods (for a reason I can't understand) this can be done with _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(); 
}
Motti