The COleVariant Class
Writing a C++ class to wrap the VARIANT structure makes a lot of sense. Constructors can call VariantInit, and the destructor can call VariantClear. The class can have a constructor for each standard type, and it can have copy constructors and assignment operators that call VariantCopy. When a variant object goes out of scope, its destructor is called and memory is cleaned up automatically.
The MFC team created such a class that works well in Automation clients and components. A simplified declaration is shown here:
class COleVariant : public tagVARIANT
{
// Constructors
public:
COleVariant();
COleVariant(const VARIANT& varSrc);
COleVariant(const COleVariant& varSrc);
COleVariant(LPCTSTR lpszSrc);
COleVariant(CString& strSrc);
COleVariant(BYTE nSrc);
COleVariant(short nSrc, VARTYPE vtSrc = COleVariant(long lSrc, VARTYPE vtSrc =
COleVariant(float fltSrc);
COleVariant(double dblSrc);
COleVariant(const COleDateTime& dateSrc);
// Destructor
~COleVariant(); // deallocates BSTR
// Operations
public:
void Clear(); // deallocates BSTR
VARIANT Detach(); // more later
void ChangeType(VARTYPE vartype, LPVARIANT pSrc = >
In addition, the CArchive and CDumpContext classes have comparison operators, assignment operators, conversion operators, and friend insertion/ extraction operators. See the MFC Library Reference for a complete description of this useful MFC COleVariant class.Now let's see how the COleVariant class helps us write the component's GetFigure function that you saw referenced in the sample dispatch map. Assume that the component stores strings for four figures in a class data member:private:
CString m_strFigure[4];
Here's what we'd have to do if we used the VARIANT structure directly:VARIANT CClock::GetFigure(short n)
{
VARIANT vaResult;
::VariantInit(&vaResult);
vaResult.vt = VT_BSTR;
// CString::AllocSysString creates a BSTR
vaResult.bstrVal = m_strFigure[n].AllocSysString();
return vaResult; // Copies vaResult without copying BSTR
// BSTR still must be freed later
}
Here's the equivalent, with a COleVariant return value:VARIANT CClock::GetFigure(short n)
{
return COleVariant(m_strFigure[n]).Detach();
}
Calling the COleVariant::Detach function is critical here. The GetFigure function constructs a temporary object that contains a pointer to a BSTR. That object gets bitwise-copied to the return value. If you didn't call Detach, the COleVariant destructor would free the BSTR memory and the calling program would get a VARIANT that contained a pointer to nothing.A component's variant dispatch function parameters are declared as const VARIANT&. You can always cast a VARIANT pointer to a COleVariant pointer inside the function. Here's the SetFigure function:void CClock::SetFigure(short n, const VARIANT& vaNew)
{
COleVariant vaTemp;
vaTemp.ChangeType(VT_BSTR, (COleVariant*) &vaNew);
m_strFigure[n] = vaTemp.bstrVal;
}
Client dispatch function variant parameters are also typed as const VARIANT&. You can call those functions with either a VARIANT or a COleVariant object. Here's an example of a call to the CClockDriver::SetFigure function:
pClockDriver->SetFigure(0, COleVariant("XII"));
Note | You can also use the standard classes _bstr_t and _variant_t to support BSTR and VARIANT. These classes are independent of the MFC library. The _bstr_t class encapsulates the BSTR data type; the _variant_t class encapsulates the VARIANT type. Both classes manage resource allocation and deallocation. For more information on these classes, see the Visual C++ .NET online documentation. |
Parameter and Return Type Conversions for Invoke
All IDispatch::Invoke parameters and return values are processed internally as VARIANT types. Remember that! The MFC library implementation of Invoke is smart enough to convert between a VARIANT and whatever type you supply (where possible), so you have some flexibility in declaring parameter and return types. Suppose, for example, that your controller's GetFigure function specifies the return type BSTR. If a component returns an int or a long, all is well: COM and the MFC library will convert the number to a string. Suppose your component declares a long parameter and the controller supplies an int. Again, no problem.
Note | An MFC library Automation client specifies the expected return type as a VT_ parameter to the COleDispatchDriver functions GetProperty, SetProperty, and InvokeHelper. An MFC library Automation component specifies the expected parameter types as VTS_ parameters in the DISP_PROPERTY and DISP_FUNCTION macros. |
Unlike C++, VBA is not a strongly typed language. VBA variables are often stored internally as VARIANT types. Take an Excel spreadsheet cell value, for example. A spreadsheet user can type a text string, an integer, a floating-point number, or a date/time in a cell. VBA treats the cell value as a VARIANT and returns a VARIANT object to an Automation client. If your client function declares a VARIANT return value, it can test vt and process the data accordingly.
VBA uses a date/time format that is distinct from the MFC library CTime class. Variables of type DATE hold both the date and the time in one double value. The fractional part represents time (.25 is 6:00 AM), and the whole part represents the date (the number of days since December 30, 1899). The MFC library provides a COleDateTime class that makes dates easy to deal with. You can construct a date in this way:
COleDateTime date(2001, 2, 11, 18, 0, 0);
The above declaration initializes the date to February 11, 2001, at 6:00 PM.The COleVariant class has an assignment operator for COleDateTime, and the COleDateTime class has member functions for extracting date/time components. Here's how you print the time:
TRACE("time = %d:%d:%d\n",
date.GetHour(),date.GetMinute(),date.GetSecond());
If you have a variant that contains a DATE, you use the COleVariant::ChangeType function to convert a date to a string, as shown here:
COleVariant vaTimeDate = date;
COleVariant vaTemp;
vaTemp.ChangeType(VT_BSTR, &vaTimeDate);
CString str = vaTemp.bstrVal;
TRACE("date = %s\n", str);
One last item concerning Invoke parameters: A dispatch function can have optional parameters. If the component declares trailing parameters as VARIANT types, the client doesn't have to supply them. If the client calls the function without supplying an optional parameter, the VARIANT object's vt value on the component end will be VT_ERROR.