Introducing the PROPVARIANT

Values in the property system are stored in PROPVARIANT structures. Originally constructed for use in OLE structured storage, the property system reuses this structure to hold its data.

As the name suggests, a PROPVARIANT can hold a variety of data. A VARTYPE member distinguishes what type of data a given PROPVARIANT holds. A whole slew of data members provide access to each of the types supported by a PROPVARIANT. For instance, a 4-byte signed integer has type propvar.vt=VT_I4 and can be accessed via propvar.lVal. The table below comes from propidl.idl and maps VARTYPEs to data members.

struct tag_inner_PROPVARIANT
{
VARTYPE vt;
PROPVAR_PAD1 wReserved1;
PROPVAR_PAD2 wReserved2;
PROPVAR_PAD3 wReserved3;
[switch_is((unsigned short) vt)] union
{
//
// Basic Types.
//
[case (VT_EMPTY, VT_NULL)] ;
[case (VT_I1)] CHAR cVal; // New
[case (VT_UI1)] UCHAR bVal;
[case (VT_I2)] SHORT iVal;
[case (VT_UI2)] USHORT uiVal;
[case (VT_I4)] LONG lVal;
[case (VT_UI4)] ULONG ulVal;
[case (VT_INT)] INT intVal; // New
[case (VT_UINT)] UINT uintVal; // New
[case (VT_DECIMAL, VT_I8)] LARGE_INTEGER hVal; // Decimal
[case (VT_UI8)] ULARGE_INTEGER uhVal;
[case (VT_R4)] FLOAT fltVal;
[case (VT_R8)] DOUBLE dblVal;
[case (VT_BOOL)] VARIANT_BOOL boolVal;
[case (VT_ILLEGAL)] _VARIANT_BOOL bool; // obsolete
[case (VT_ERROR)] SCODE scode;
[case (VT_CY)] CY cyVal;
[case (VT_DATE)] DATE date;
[case (VT_FILETIME)] FILETIME filetime;
[case (VT_CLSID)] CLSID * puuid;
[case (VT_CF)] CLIPDATA * pclipdata;
[case (VT_BSTR)] BSTR bstrVal;
[case (VT_BSTR_BLOB)] BSTRBLOB bstrblobVal; // System use only
[case (VT_BLOB, VT_BLOB_OBJECT)] BLOB blob;
[case (VT_LPSTR)] LPSTR pszVal;
[case (VT_LPWSTR)] LPWSTR pwszVal;
[case (VT_UNKNOWN)] IUnknown * punkVal; // New
[case (VT_DISPATCH)] IDispatch * pdispVal; // New
[case (VT_STREAM, VT_STREAMED_OBJECT)] IStream* pStream;
[case (VT_STORAGE, VT_STORED_OBJECT)] IStorage* pStorage;
[case (VT_VERSIONED_STREAM)] LPVERSIONEDSTREAM pVersionedStream;

        //
// Arrays of types (only the old VARIANT types)
//
[case (VT_ARRAY|VT_I1,
VT_ARRAY|VT_UI1,
VT_ARRAY|VT_I2,
VT_ARRAY|VT_UI2,
VT_ARRAY|VT_I4,
VT_ARRAY|VT_UI4,
VT_ARRAY|VT_INT,
VT_ARRAY|VT_UINT,
VT_ARRAY|VT_R4,
VT_ARRAY|VT_R8,
VT_ARRAY|VT_CY,
VT_ARRAY|VT_DATE,
VT_ARRAY|VT_BSTR,
VT_ARRAY|VT_BOOL,
VT_ARRAY|VT_DECIMAL,
VT_ARRAY|VT_DISPATCH,
VT_ARRAY|VT_UNKNOWN,
VT_ARRAY|VT_ERROR,
VT_ARRAY|VT_VARIANT)] LPSAFEARRAY parray; // New

        //
// Vectors of types
//
[case (VT_VECTOR|VT_I1)] CAC cac; // new
[case (VT_VECTOR|VT_UI1)] CAUB caub;
[case (VT_VECTOR|VT_I2)] CAI cai;
[case (VT_VECTOR|VT_UI2)] CAUI caui;
[case (VT_VECTOR|VT_I4)] CAL cal;
[case (VT_VECTOR|VT_UI4)] CAUL caul;
[case (VT_VECTOR|VT_I8)] CAH cah;
[case (VT_VECTOR|VT_UI8)] CAUH cauh;
[case (VT_VECTOR|VT_R4)] CAFLT caflt;
[case (VT_VECTOR|VT_R8)] CADBL cadbl;
[case (VT_VECTOR|VT_BOOL)] CABOOL cabool;
[case (VT_VECTOR|VT_ERROR)] CASCODE cascode;
[case (VT_VECTOR|VT_CY)] CACY cacy;
[case (VT_VECTOR|VT_DATE)] CADATE cadate;
[case (VT_VECTOR|VT_FILETIME)] CAFILETIME cafiletime;
[case (VT_VECTOR|VT_CLSID)] CACLSID cauuid;
[case (VT_VECTOR|VT_CF)] CACLIPDATA caclipdata;
[case (VT_VECTOR|VT_BSTR)] CABSTR cabstr;
[case (VT_VECTOR|VT_BSTR_BLOB)]CABSTRBLOB cabstrblob; // System use only
[case (VT_VECTOR|VT_LPSTR)] CALPSTR calpstr;
[case (VT_VECTOR|VT_LPWSTR)] CALPWSTR calpwstr;
[case (VT_VECTOR|VT_VARIANT)] CAPROPVARIANT capropvar;

        //
// ByRefs of types.
//
[case (VT_BYREF|VT_I1)] CHAR* pcVal; // New
[case (VT_BYREF|VT_UI1)] UCHAR* pbVal; // New
[case (VT_BYREF|VT_I2)] SHORT* piVal; // New
[case (VT_BYREF|VT_UI2)] USHORT* puiVal; // New
[case (VT_BYREF|VT_I4)] LONG* plVal; // New
[case (VT_BYREF|VT_UI4)] ULONG* pulVal; // New
[case (VT_BYREF|VT_INT)] INT* pintVal; // New
[case (VT_BYREF|VT_UINT)] UINT* puintVal; // New
[case (VT_BYREF|VT_R4)] FLOAT* pfltVal; // New
[case (VT_BYREF|VT_R8)] DOUBLE* pdblVal; // New
[case (VT_BYREF|VT_BOOL)] VARIANT_BOOL* pboolVal; // New
[case (VT_BYREF|VT_DECIMAL)] DECIMAL* pdecVal; // New
[case (VT_BYREF|VT_ERROR)] SCODE* pscode; // New
[case (VT_BYREF|VT_CY)] CY* pcyVal; // New
[case (VT_BYREF|VT_DATE)] DATE* pdate; // New
[case (VT_BYREF|VT_BSTR)] BSTR* pbstrVal; // New
[case (VT_BYREF|VT_UNKNOWN)] IUnknown ** ppunkVal; // New
[case (VT_BYREF|VT_DISPATCH)] IDispatch ** ppdispVal; // New
[case (VT_BYREF|VT_ARRAY)] LPSAFEARRAY* pparray; // New
[case (VT_BYREF|VT_VARIANT)] PROPVARIANT* pvarVal; // New
};
};

The property system directly supports those VARTYPEs marked in red. The property system coercion layer supports conversions to and from additional other types, but it is simplest if callers and callees use the correct type for each property.

There are a number of idioms that I'll try to cover in this blog that should keep you away from trouble. Until then, there are two very important things to remember about PROPVARIANTs.

  1. First, they are complex data types. Code should be written defensively just in case the type of data is not what you expected. 
  2. Second, they often contain allocated data. This means your code must maintain a clear sense of who owns the data and who is responsible for cleaning them up. 

[Ben 2006/09/14: Corrected PROPVARIANT link]