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]