Information on Variants and Safe Arrays ComponentWorks Support Note 010

Information on Variants and Safe Arrays

Variants and Safe Arrays are two special data types used in Visual C++ programming. These data types are used in the ComponentWorks source code and are also used when working with ComponentWorks controls in Visual C++. Additionally variants also exist in other programming environments such as Visual Basic and Borland Delphi and are used with the ComponentWorks controls in these environments. This support note explains these two special data types and their use in Visual C++.

Variants

A variant is a variable which stores data of varying data types such as integers, floating point numbers, strings and arrays. Internally a variant is actually an object that contains two member variables. One member variable (vt) stores information that specifies the data type of the variant. The second member variable stores the value of the variant. This second member variable is a union of variables of all the different possible data types of a variant. The union is the mechanism that allows the variant to store data of many different data types. The names of the union member variables necessary for accessing the variant data are given below.

A union is a collection of variables of different data types where each variable has its own name and only one of these variables is being used at a time. Internally the amount of memory allocated for the union is equal to the size of the largest variable of the union. Regardless of what variable of the union is used it is always stored in the common data space and referenced by the unique name of the variable.

When a variant variable is initially declared it has no defined data type. The data type and data value are assigned explicitly in the code where the variant variable is used. It can also be redefined to a different data type later on in the code.

Information from the Visual C++ manuals:

The VARIANT type is a tagged union. It has a data member for the value, this is an anonymous C++ union, and a data member indicating the type of information stored in the union. The VARIANT type supports a number of standard data types: 2- and 4-byte integers, 4- and 8-byte floating point numbers, strings, and Boolean values. In addition, it supports the HRESULT (OLE error codes), CURRENCY (a fixed-point numeric type), and DATE (absolute date and time) types, as well as pointers to IUnknown and IDispatch interfaces.

Variant Definition:

typedef struct FARSTRUCT tagVARIANT VARIANT;

typedef struct tagVARIANT {

VARTYPE vt;

unsigned short wReserved1;

unsigned short wReserved2;

unsigned short wReserved3;

union {

unsigned char bVal; /* VT_UI1 =17 */

short iVal; /* VT_I2 = 2 */

long lVal; /* VT_I4 = 3 */

float fltVal; /* VT_R4 = 4 */

double dblVal; /* VT_R8 = 5 */

VARIANT_BOOL bool; /* VT_BOOL=11 */

SCODE scode; /* VT_ERROR=10 */

CY cyVal; /* VT_CY = 6 */

DATE date; /* VT_DATE= 7 */

BSTR bstrVal; /* VT_BSTR= 8 */

Iunknown FAR* punkVal; /* VT_UNKNOWN =13 */

Idispatch FAR* pdispVal; /* VT_DISPATCH= 9 */

SAFEARRAY FAR* parray; /* VT_ARRAY|ANYTHING */

unsigned char FAR *pbVal; /* VT_BYREF|VT_UI1 */

short FAR* piVal; /* VT_BYREF|VT_I2 */

long FAR* plVal; /* VT_BYREF|VT_I4 */

float FAR* pfltVal; /* VT_BYREF|VT_R4 */

double FAR* pdblVal; /* VT_BYREF|VT_R8 */

VARIANT_BOOL FAR* pbool; /* VT_BYREF|VT_BOOL */

SCODE FAR* pscode; /* VT_BYREF|VT_ERROR */

CY FAR* pcyVal; /* VT_BYREF|VT_CY */

DATE FAR* pdate; /* VT_BYREF|VT_DATE */

BSTR FAR* pbstrVal; /* VT_BYREF|VT_BSTR */

IUnknown FAR* FAR* ppunkVal; /* VT_BYREF|VT_UNKNOWN */

IDispatch FAR* FAR* ppdispVal; /* VT_BYREF|VT_DISPATCH */

SAFEARRAY FAR* FAR* parray; /* VT_ARRAY|* */

VARIANT FAR* pvarVal; /* VT_BYREF|VT_VARIANT */

void FAR* byref; /* Generic ByRef */

};

};

VARTYPE (the data type of vt in a variant) is an enumeration type used in VARIANT, and other OLE Automation datatypes. For each of the fields listed above, the corresponding value of vt is specified in the comments to the right.

Additional enumerations that may be used to specify the type of a VARIANT:

VT_EMPTY = 0 // Not specified

VT_NULL = 1 // Null

VT_RESERVED= (int)0x8000

VT_BYREF = (int)0x4000 // Stores a pointer to the data

VT_ARRAY = (int)0x2000 // Data is contained in a safearray

The vartype of a VARIANT can be the bit-wise OR of two data types. This is common when the VARIANT stores an array, or a reference to a value.

For an array of doubles, vt = VT_ARRAY | VT_R8.

In hexidecimal notation, x2000 | x0005 results in a vt of x2005.

In decimal notation, 8192 | 5 which is 8197.

Useful Functions for manipulating a VARIANT

HRESULT VariantInit(VARIANT * pvarg);

Initializes a VARIANT

HRESULT VariantClear(VARIANT * pvarg);

Clears a VARIANT. Frees array data if the VARIANT contains a SAFEARRAY.

HRESULT VariantChangeType(VARIANT * pvargDest, VARIANT * pvarSrc, shore wFlags, VARTYPE vt);

Changes the type of a VARIANT. The only legal value for wFlags at this time is VARIANT_NOVALUEPROP.

Example: Create a VARIANT that contains the integer 2.

VARIANT v;

VariantInit(&v);

v.vt = VT_I4;

V_I4(&v) = 2; // V_I4 is a macro that accesses the correct field in // the union for type VT_I4. All VT_TYPE types have

// corresponding V_TYPE accessors

Example: Change the data stored in a VARIANT from a double to a long

//m_Divisior is a NumEdit control, which stores the data as a double

VARIANT divisor = m_Divisor.GetValue();

HRESULT result;

result = VariantChangeType( &divisor, &divisor, VARIANT_NOVALUEPROP,

VT_I4 );

//divisor.vt = VT_I4, and the data is stored in divisor.lval

Example: Chart data in a ComponentWorks Graph using VARIANTs

// definition of PlotY method on CWGraph in CWGRAPH.CPP

void CCWGraph::PlotY(const VARIANT& yData, const VARIANT& xFirst, const VARIANT& xInc, const VARIANT& bPlotPerRow)

.

#include "stdafx.h" // include file for Variants

// use of Variants in PlotY method of graph

// Voltages is a variant pointer passed into this routine and is

// dereferenced before passing to PlotY

void CMyDlg::OnAcquiredDataCwai1(VARIANT FAR* Voltages, VARIANT FAR* BinaryCodes)

{

VARIANT v0;

VARIANT v1;

double sum = 0;

double meanValue;

v0.vt = VT_I4; // assign variant data type

v1.vt = VT_I4; // assign variant data type

v0.lVal = 0; // assign variant value (long int)

v1.lVal = 1; // assign variant value (long int)

m_Graph1.PlotY (*Voltages, v0, v1, v1);

}

Files used to declare and define variants:

AFXWIN.H. and 0AIDL.H contain the declarations for VARIANT, VARTYPE, and VARENUM. These header files are included in STDAFX.H, which is included in most VC++ projects by default.

SafeArray

A safe array is a class that encapsulates an array of any data type and provides certain protective features. The SAFEARRAY structure provides functions to manipulate safe arrays. The structure SAFEARRAYBOUND is used to specify the dimensions and size of a SAFEARRAY variable. To allocate a SAFEARRAY object you first define a SAFEARRAYBOUND object and then create the SAFEARRAY object.

Information from the Visual C++ manuals:

SAFEARRAY

typedef struct FARSTRUCT tagSAFEARRAY {

unsigned short cDims; // Count of dimensions in this array.

unsigned short fFeatures; // Flags used by the SafeArray

#if defined(WIN32)

unsigned long cbElements; // Size of an element of the array;

// Does not include size of

// pointed-to data.

unsigned long cLocks; // Number of times the array has been

// locked without corresponding unlock.

#else

unsigned short cbElements;

unsigned short cLocks;

unsigned long handle; // Unused but kept for compatibility

#endif

void HUGEP* pvData; // Pointer to the data.

SAFEARRAYBOUND rgsabound[1]; // One bound for each dimension.

} SAFEARRAY;

Note that the definition varies, depending on the target operating system platform. On 32-bit Windows systems, both the cbElements and cLocks parameters are unsigned long integers, and the handle parameter is omitted. On 16-bit Windows systems, cbElements and cLocks are unsigned short integers, and the handle parameter has been retained for compatibility with earlier software.

SAFEARRAYBOUND

typedef struct tagSAFEARRAYBOUND {

unsigned long cElements;

long lLbound;

} SAFEARRAYBOUND;

This structure represents the bounds of one dimension of the array. The lower bound of the dimension is represented by lLbound, and cElements represents the number of elements in the dimension. The array rgsabound is stored with the leftmost dimension in rgsabound[0] and the rightmost dimension in rgsabound[cDims  1]. If an array were specified in C-like syntax as a[2][5], it would have two elements in the rgsabound vector. Element 0 has an lLbound of 0 and a cElements of 2. Element 1 has an lLbound of 0 and a cElements of 5.

Useful functions for dealing with the SAFEARRAY structure:

HRESULT SafeArrayCreate(VARTYPE vt, unsigned int cDims, SAFEARRAYBOUND * rgsabound);

Creates a new array descriptor, allocates and initializes the data for the array, and returns a pointer to the new array descriptor.

HRESULT SafeArrayDestroy(SAFEARRAY * psa);

Destroys an existing array descriptor and all of the data in the array. If objects are stored in the array, Release is called on each object in the array.

HRESULT SafeArrayGetElement(SAFEARRAY * psa, long * rgIndices, void * pv);

Retrieves a single element of the array.

HRESULT SafeArrayPutElement(SAFEARRAY * psa, long * rgIndices, void * pv);

Assigns a single element to the array.

HRESULT SafeArrayAccessData(SAFEARRAY * psa, void ** ppvData);

Retrieves a pointer to the array data.

HRESULT SafeArrayUnaccessData(SAFEARRAY * psa);

Invalidates the pointer retrieved by SafeArrayAccessData.

Example: Create a SAFEARRAY and manipulate the elements

SAFEARRAY * psa; // The safearray

SAFEARRAYBOUND rgsabound[1]; // A one dimensional array

long * pData;

long lValue, lIndex;

rgsabound[0].lLbound = 0; // With lower bound 0;

rgsabound[0].cElements = 10; // With 10 elements;

psa = SafeArrayCreate(VT_I4, 1, rgsabound); // Create an array

// of integers

SafeArrayAccessData(psa,(void*)&pData); // Get a pointer to the data

for (int i=0;i<10;++i)

pData[i] = i; // Fill array with values from 0 to 9

SafeArrayUnaccessData(psa); // pData is now invalid.

lIndex = 5;

SafeArrayGetElement(psa,&lIndex,(void*)&lValue); // lValue is now 5

++lValue; // lValue is now 6

SafeArrayPutElement(psa,&lIndex,(void*)&lValue); // psa[5] is now 6

SafeArrayDestroy(psa); // Destroy the SAFEARRAY and free memory

Example: Create a SAFEARRAY of random numbers and display on CWGraph

void CVisualCGuiExample1Dlg::OnTimer(UINT nIDEvent)

{ VARIANT v;

v = m_slide2.GetValue();

VariantChangeType(&v, &v, 0, VT_I4);

long cPtsPerTrace = __max(v.lVal, 1);

v = m_slide1.GetValue();

VariantChangeType(&v, &v, 0, VT_I4);

long cTraces = __max(v.lVal, 1);

const kcDims = 2;

SAFEARRAYBOUND sa[kcDims];

sa[1].cElements = cPtsPerTrace;

sa[1].lLbound = 0;

sa[0].cElements = cTraces;

sa[0].lLbound = 0;

long rgIndices[kcDims];

double d;

SAFEARRAY *psa = SafeArrayCreate(VT_R8, kcDims, sa);

BOOL bStacked = m_btnStacked.GetValue();

for (long lTrace = 0; lTrace < cTraces; lTrace++) {

for (long lPt = 0; lPt < cPtsPerTrace; lPt++) {

rgIndices[1] = lPt;

rgIndices[0] = lTrace;

// random double between 0 and 10

const double kdRange = 10.0;

d = (double)rand() / RAND_MAX * kdRange;

if (bStacked) {

d = d / cTraces + kdRange / cTraces * lTrace;

}

VERIFY(SafeArrayPutElement(psa, rgIndices, &d) == S_OK);

}

}

VARIANT vOptional, vArray;

vOptional.vt = VT_ERROR;

vOptional.scode = DISP_E_PARAMNOTFOUND;

vArray.vt = VT_ARRAY | VT_R8; // array of doubles

vArray.parray = psa; // store the safe array in a variant

m_graph.PlotY(vArray, vOptional, vOptional, vOptional);

VERIFY(SafeArrayDestroy(psa) == S_OK); //destroy the safe array

CDialog::OnTimer(nIDEvent);

}
MFC Wrapper Classes

Visual C++ includes two wrapper classes that simplify interactions with the VARIANT and SAFEARRAY structures.

COleVariant

A COleVariant object encapsulates the VARIANT data type. You can pass a COleVariant in a parameter that calls for a VARIANT and the data members of the VARIANT structure are accessible data members of COleVariant.

ColeVariant class includes member functions for accessing Variants.

You can automatically create a ColeVariant, specify the type and the value in one step:

ColeVariant vLong((long)3.0); //create a variant with type VT_I4 and value 3.0

To pass optional parameters to ComponentWorks methods, initialize a VARIANT with the error value PARAMNOTFOUND. A parameter with this type will cause the function to use the default value.

ColeVariant vOpt((long)DISP_E_PARAMNOTFOUND, VT_ERROR)

COleSafeArray

Class COleSafeArray is a class for working with arrays of arbitrary type and dimension. COleSafeArray derives from the OLE VARIANT structure. The OLE SAFEARRAY member functions are available through COleSafeArray, as well as a set of member functions specifically designed for one-dimensional arrays of bytes.

ComponentWorks Wrapper Classes

ComponentWorks ships with its own wrapper classes for the VARIANT and SAFEARRAY structures. These classes are used frequently in our VC++ examples.

ComponentWorks\Samples\Visual C++\Lib contains cwvariant.h, cwvariant.cpp, cwsafearray.h and cwsafearray.cpp, and a sample program in the SafeArray Sample folder.

CCWVariant

Includes member functions that create and initialize the underlying VARIANT, and easily change the type stored in the VARIANT.

CCWSafeArray

Includes member functions that initialize a SafeArray object from an existing array, and functions for returning information about the array such as GetDimSizes, GetEltSize, GetNDims.

To use the CCWVariant and CCWSafeArray classes in a VC project, copy the files to your working folder, add the files to the project(Project>Add to Project>Files), and #include them in the main .cpp file.

pg 7