contents   index   previous   next



The definition of a Variant

 

A Variant is defined as a structure that uses a total of 16 bytes. In C, this structure would look as follows:

 

struct tagVARIANT {
    union {
        struct __tagVARIANT {
            VARTYPE vt;
            WORD    wReserved1;
            WORD    wReserved2;
            WORD    wReserved3;
            union {
                LONG          lVal;         /* VT_I4                */
                BYTE          bVal;         /* VT_UI1               */
                SHORT         iVal;         /* VT_I2                */
                FLOAT         fltVal;       /* VT_R4                */
                DOUBLE        dblVal;       /* VT_R8                */
                VARIANT_BOOL  boolVal;      /* VT_BOOL              */
                _VARIANT_BOOL bool;         /* (obsolete)           */
                SCODE         scode;        /* VT_ERROR             */
                CY            cyVal;        /* VT_CY                */
                DATE          date;         /* VT_DATE              */
                BSTR          bstrVal;      /* VT_BSTR              */
                IUnknown *    punkVal;      /* VT_UNKNOWN           */
                IDispatch *   pdispVal;     /* VT_DISPATCH          */
                SAFEARRAY *   parray;       /* VT_ARRAY             */
                BYTE *        pbVal;        /* VT_BYREF|VT_UI1      */
                SHORT *       piVal;        /* VT_BYREF|VT_I2       */
                LONG *        plVal;        /* VT_BYREF|VT_I4       */
                FLOAT *       pfltVal;      /* VT_BYREF|VT_R4       */
                DOUBLE *      pdblVal;      /* VT_BYREF|VT_R8       */
                VARIANT_BOOL *pboolVal;     /* VT_BYREF|VT_BOOL     */
                SCODE *       pscode;       /* VT_BYREF|VT_ERROR    */
                CY *          pcyVal;       /* VT_BYREF|VT_CY       */
                DATE *        pdate;        /* VT_BYREF|VT_DATE     */
                BSTR *        pbstrVal;     /* VT_BYREF|VT_BSTR     */
                IUnknown **   ppunkVal;     /* VT_BYREF|VT_UNKNOWN  */
                IDispatch **  ppdispVal;    /* VT_BYREF|VT_DISPATCH */
                SAFEARRAY **  pparray;      /* VT_BYREF|VT_ARRAY    */
                VARIANT *     pvarVal;      /* VT_BYREF|VT_VARIANT  */
                PVOID         byref;        /* Generic ByRef        */
                CHAR          cVal;         /* VT_I1                */
                USHORT        uiVal;        /* VT_UI2               */
                ULONG         ulVal;        /* VT_UI4               */
                INT           intVal;       /* VT_INT               */
                UINT          uintVal;      /* VT_UINT              */
                DECIMAL *     pdecVal;      /* VT_BYREF|VT_DECIMAL  */
                CHAR *        pcVal;        /* VT_BYREF|VT_I1       */
                USHORT *      puiVal;       /* VT_BYREF|VT_UI2      */
                ULONG *       pulVal;       /* VT_BYREF|VT_UI4      */
                INT *         pintVal;      /* VT_BYREF|VT_INT      */
                UINT *        puintVal;     /* VT_BYREF|VT_UINT     */
            } __VARIANT_NAME_3;
        } __VARIANT_NAME_2;
        DECIMAL decVal;
    } __VARIANT_NAME_1;
};

 

Before you get a headache trying to decipher this structure, you should be aware that you don’t need to know all its details to use Variants. However, as with almost everything, you can make a more proficient use of the tools available to you if you have a good understanding of how these tools work. So in this section we’ll dissect the structure shown above and explain what each portion is and how to use it. Rather than using the C structure above, we’ll use the equivalent Fortran structure. If you check the source code in f90VBDefs.f90, you’ll see that the type variant is defined as:

 

    type VARIANT
        union [14]
            map 
                type(tagVARIANT)::varVal
            end map
            map 
                type(DECIMAL)::decVal
            end map
        end union
    end type VARIANT

 

So you see that a Variant is actually two structures, tagVARIANT and DECIMAL, sharing the same memory space. The tagVARIANT structure has the following Fortran definition:

 

    type tagVARIANT
        sequence
        integer(VARTYPE_KIND)::vt
        integer(WORD_KIND)::   wReserverd1
        integer(WORD_KIND)::   wReserverd2
        integer(WORD_KIND)::   wReserverd3
        union
            map
                integer(BYTE_KIND)::bVal 
            end map
            map
                integer(SHORT_KIND)::iVal 
            end map
            map
                integer(LONG_KIND)::lVal  
            end map
            map
                real(FLOAT_KIND)::fltVal 
            end map
            map
                real(DOUBLE_KIND)::dblVal 
            end map
            !many other maps removed for clarity
        end union
end type tagVARIANT

 

A tagVARIANT structure has a fixed section containing three fields (vt, wReserved1, wReserved2 and wReserved3), and a shared-memory section described by a union statement containing many other fields (bVal, iVal, lVal, fltVal, etc.). The fixed section has a total length of eight bytes (in f90VB, VARTYPE_KIND and WORD_KIND are used to identify two-byte integers). The number of bytes used by the shared-memory section is given by the size of the largest type in the union; in this case a double-precision real. So the shared-memory section of tagVARIANT also has eight-bytes, and the total number of bytes used by a tagVARIANT structure is sixteen.

 

The tagVARIANT structure is the main structure used to store Variant values. The union portion of the structure contains the actual value of the Variant, while the vt field indicates the type of value stored in the union. The three reserved fields following vt are not used in normal variants [15]. To clarify the way this works, let’s say you have a Fortran subroutine that is provided with a variant argument:

 

subroutine MyFortranSub(VarArg)

 

   !subroutine arguments

 

   Type(VARIANT),intent(in)::VarArg

 

   !internal variables
   integer(SHORT_KIND)::shortVal
   integer(LONG_KIND)::longVal
   real(FLOAT_KIND)::floatVal
   real(DOUBLE_KIND)::doubleVal
   …

 

end subroutine MyFortranSub

 

To retrieve the value in the variant, your subroutine could implement the following code:

 

select case (VarArg%varVal%vt)
   case (VT_I2, VT_UI2)
      shortVal = VarArg%varVal%iVal
   case (VT_I4, VT_UI4)
      longVal = VarArg%varVal%lVal
   case (VT_R4)
      floatVal = VarArg%varVal%fltVal
   case (VT_R8)
      doubleVal = VarArg%varVal%dblVal
   case ELSE
      !this subroutine cannot handle other argument types
end select 

 

Note that we test field vt against a set of pre-determined constants. In standard COM/OLE (and Visual Basic), a variant can contain only some pre-determined types of data [16], also known as VT-Types. We introduced VT-Types in chapter 2, because they are also used to represent the type of the elements of a Safe Array. Table 3.1 describes all the allowed values for field vt and the data type a Variant with this VT-Type would contain [17].

 

You should be aware that the last three VT-Types shown in Table 3.1 do not represent data types, but instead are modifiers of the base data type, to indicate that the Variant does not contain the data but a pointer to the data. So a variant with a VT-Type of 8195 (VT_ARRAY+VT_I4) contains a handle to a Safe Array whose elements are of VT-Type VT_I4 (i.e. four-byte integers).

 

As explained earlier, the tagVARIANT structure shares its memory with a DECIMAL structure. The DECIMAL structure is used to represent very large numbers with a high level of precision (up to 28 decimal digits). The DECIMAL structure is defined as follows:

 

    type DECIMAL
        sequence   
        integer(SHORT_KIND)::wReserved   
        integer(BYTE_KIND)::scale   
        integer(BYTE_KIND)::sign   
        integer(LONG_KIND)::Hi32   
        integer(LONG_KIND)::Lo32   
        integer(LONG_KIND)::Mid32
    end type DECIMAL

 

Note that because a DECIMAL and a tagVARIANT share the same memory locations, the first field of a DECIMAL structure (i.e wReserved) coincides with the vt field of the tagVARIANT structure. When a Variant contains a DECIMAL value, the vt field of tagVARIANT is set to VT_DECIMAL (so if you read wReserved from the DECIMAL structure, you would also get VT_DECIMAL). The field scale is used to indicate the position of the decimal point in the value represented by the DECIMAL structure; it can be any value between 0 and 28. The field sign indicates the sign of the value stored in the DECIMAL structure (0 for positive, DECIMAL_NEG for negative values). The other 12 bytes (contained in fields Hi32, Lo32 and Mid32) store the value represented by the structure. Note that although f90VB allows you to use the DECIMAL to perform basic arithmetic operations, DECIMAL operations are very slow, so you should avoid their use unless absolutely necessary.

 

In summary, you have seen that a Variant can be a tagVARIANT or a DECIMAL, depending on the value contained in its first two bytes. You can access this value by either reading the vt field from the tagVARIANT structure, or by reading the wReserved field from the DECIMAL structure. In most cases, you will be working with the tagVARIANT structure. In these cases, you can always know the content of a variant (and the field you should use to access this content) by testing the value stored in the vt field.

 

Now that we have dispelled the magic behind the Variant data type, the following sections of this chapter explain how to use the subroutines and functions provided by f90VB to manipulate Variants.

 

7 basic rules for manipulating Variants