Wrapper Classes

The following are illustrated in this section
__declspec (property)
Interface Pointers
    CComPtr<>
    _com_ptr_t<>
Strings
    Unicode
    Prerequisites
    BSTR
    _bstr_t
    CComBSTR
Variants
    VARIANT
    _variant_t
    CComVariant
SAFEARRAY
Exceptions

__declspec( property ... )

__declspec(property()) declares a non-static data member as a virtual data member by associating it with public accessor (Get) and mutator (Set) methods.

// CAR.H
class CCar 
{
private:
    int nSpeed;
    int nItems[10];
public:
    CCar(); 
    virtual ~CCar();

    __declspec(property(get=GetSpeed,put=SetSpeed)) int Speed;
    int GetSpeed();
    void SetSpeed( int nNum );

    __declspec(property(get=GetItem,put=SetItem)) int Items[];
    int GetItem( long nIndex);
    void SetItem( long nIndex, long nVal);
};

// CAR.CPP

#include "stdafx.h"
#include "car.h"

/* Using __declspec(property()) improves readability. Consider the following -
CCar     ob;
ob.SetItem( 0, 20);
ob.Items[0] = 20;     // uses __declspec( property() ) */

CCar::CCar() : nSpeed(0) {}

CCar::~CCar() {}

int CCar::GetSpeed()
{
    return nSpeed;
}

void CCar::SetSpeed( int nNum )
{
    nSpeed = nNum;
}

int CCar::GetItem( long nIndex)
{
    return nItems[nIndex];
}
void CCar::SetItem( long nIndex, long nVal)
{
    nItems[nIndex] = nVal;
}

Interface Pointers

#include "stdafx.h"
#include <comdef.h>     // Brings in IDispatch definition

struct __declspec(uuid("aeca53e3-f3a6-11d3-8eb4-000000000000")) Object1;
struct __declspec(uuid("9d5ace00-f934-11d3-8ebc-000000000000")) IEngine : IDispatch {};
struct __declspec(uuid("aeca53e2-f3a6-11d3-8eb4-000000000000")) IObject1 : IDispatch
{
    virtual HRESULT __stdcall get_Number ( long * pVal ) = 0;
    virtual HRESULT __stdcall put_Number ( long pVal ) = 0;
    virtual HRESULT __stdcall raw_SomeFunc ( long nNumber ) = 0;
    virtual HRESULT __stdcall raw_GetEngine ( struct IEngine ** ppEngine ) = 0;
};

void RawInterfacePointers()

    HRESULT hr;
    IObject1 *pItf = NULL;
    IUnknown *pUnk = NULL;

    // Create the 'Object1' COM object
    hr = CoCreateInstance
            ( 
            __uuidof(Object1), // aeca53e3-f3a6-11d3-8eb4-000000000000
            NULL,
            CLSCTX_ALL,
            __uuidof( IObject1), // aeca53e2-f3a6-11d3-8eb4-000000000000
            reinterpret_cast<void**>(&pItf)
            );

    // Call some functions on the newly created object
    hr = pItf->raw_SomeFunc( 10 );

    /* This cast slices off IObject1 methods. Do not do this! Note that pUnk 
    will get the same pointer value as pItf (casting a pointer to another does 
    not change the pointer value */
 
   pUnk = static_cast<IUnknown*>(pItf);

    /* Although the two pUnk and pUnk2 interfaces are equal, we should only 
    retrieve IUnknown from QueryInterface(). On the same note, to compare 
    if two interface point to the same object, QueryInterface() for IUnknown
    pointer and compare the two resulting pointers. Never compare two 
    IUnknown's for object identity if they were obtained from a cast */

    IUnknown *pUnk2 = NULL;
    pItf->QueryInterface( __uuidof(IUnknown), (void**)&pUnk2);

    // Clean up
    pItf->Release();
    pUnk2->Release();
}

#import "Generic.tlb" no_namespace 

void CreateObjectWithTypeLib()

    long     nNum = 10;
    HRESULT  hr;

    /* Standard way to create an object and retrieve a pointer to it. To remember 
    what to supply as argument, recall that retrieving an interface pointer 
    requires the CLSID of its coclass. Therefore, pass the CLSID. */

    IObject1Ptr p( __uuidof(Object1) );

    /* Two ways to access the same METHOD */
    hr = p->SomeFunc( nNum );     // high-level method that wraps COM errors
    hr = p->raw_SomeFunc( nNum ); // raw interface method 

    /* Three ways to access the same PROPERTY */
   
hr   = p->get_Number( &nNum); // raw interface method
    nNum = p->GetNumber();        // high-level method that wraps COM errors
    nNum = p->Number;             // virtual member variable using __declspec(property())

    /* Using __uuidof() */
    CLSID clsidObject = __uuidof( Object1) ;  // Using the CoClass class
    IID iidID1        = __uuidof( IObject1 ); // Using the interface class
    IID iidID2        = __uuidof( p );        // Using interface instance

    /* GetEngine() retrieves an interface pointer via an [out] param, which 
    implies that AddRef() has been called from within GetEngine(). The 
    implementation of GetEngine() has the following code (from the .tli) -
        inline IEnginePtr IObject1::GetEngine ( )
        {
            struct IEngine * _result;
            HRESULT _hr = raw_GetEngine(&_result);
            if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
            return IEnginePtr(_result, false);
        }

    IEnginePtr is created with a call to the constructor that will NOT call
    AddRef(). When execution moves to the next line (the end of GetEngine), the 
    temp object is destroyed and its destructor calls Release() soaking up the 
    extra reference count from the assignment operator. (the assignment operator
    is called BEFORE the temp object is destroyed). Therefore, when pEngine is 
    destroyed, the Release() will be matched by the AddRef() from GetEngine()
    */

    IEnginePtr pEngine;
    pEngine = p->GetEngine();

    /* We can release pEngine by letting it go out of scope OR by calling its
    Release() function */
    pEngine.Release();         // NEVER: pEngine->Release(); NO NO NO
}

 
CComPtr<> & CComQIPtr

// Object1 object
struct __declspec(uuid("aeca53e3-f3a6-11d3-8eb4-000000000000")) Object1;

// IEngine Interface
struct __declspec(uuid("9d5ace00-f934-11d3-8ebc-000000000000")) IEngine : IDispatch
{};

// IObject1 Interface
struct __declspec(uuid("aeca53e2-f3a6-11d3-8eb4-000000000000")) IObject1 : IDispatch
{
    virtual HRESULT __stdcall get_Number ( long * pVal ) = 0;
    virtual HRESULT __stdcall put_Number ( long pVal ) = 0;
    virtual HRESULT __stdcall raw_SomeFunc ( long nNumber ) = 0;
    virtual HRESULT __stdcall raw_GetEngine ( struct IEngine ** ppEngine ) = 0;
};

void CreateObjectCComPtr()
{
    long nNum;

    {
        CComQIPtr<IObject1, &__uuidof(IObject1)> sp1;     // REF COUNT = 1
        sp1.CoCreateInstance( __uuidof(Object1) );
        sp1->put_Number( 10 );
        sp1->get_Number( &nNum );

        /* Calling Release() is required if you want to reuse the smart 
        pointer. Note that ~CComQIPtr<> is called and it will release the 
        wrapped pointer if it is not already release
            sp1.Release();      // Optional. Can do without.
            sp1->Release();     // Will not compile. See note above
        */

        /* Some manipulations. 
        Note that the last statement calls the constructor explicitly. Just 
        like -
        CString str1 = CString("hello");     // OR same as
        CString str1( CString ("hello") );

        In essence, the right side of the last statement acts just like a 
        casting operator for interfaces - it converts the given interface
        pointer to the required type using CComQIPtr<> which will do a QI()
        based on the supplied type. Note that the second argument for 
        CComQIPtr<> defaults to __uuidof(T)

        'CComPtr<IObject1> sp2 = CComQIPtr<IObject1>(pUnk)'  calls these 
        functions:
        CComQIPtr::CComQIPtr(IUnknown* lp)  // get IObject1 interface from pUnk
        CComQIPtr::operator T*()            // conversion operator - returns raw ptr
        CComPtr( T * )                      // sp2 is initialied with the raw ptr
        CComQIPtr::~CComQIPtr()             // destroy temp object

        Consider this example to illustrate the conversion function
        class Money
        {
        public:
            Money();
            operator double() { return _amount; }
        private:
            double _amount;
        };

        Money Account;
        double CachOnHand = Account;    // calls operator double() to perform a
                                        // conversion from type Account to type double.
        */

        IUnknown *pUnk = NULL;
        sp1.QueryInterface( &pUnk );         // AddRef()d. Must release! REF COUNT = 2
        CComPtr<IObject1> sp2 = CComQIPtr<IObject1>(pUnk); // REF COUNT = 3

        /* This should help explain the previous code snippet 
        'IObject1 *pOb1 = CComQIPtr<IObject1>(pUnk)' calls these functions:
            CComQIPtr::CComQIPtr(IUnknown* lp) // get IObject1 interface from pUnk
            CComQIPtr::operator T*()           // conversion operator - returns raw ptr 
            CComQIPtr::~CComQIPtr()            // destroy temp object
        */

        IObject1 *pOb1 = CComQIPtr<IObject1>(pUnk); // Ref count not AddRef()ed
        pUnk->Release(); // REF COUNT = 2

        // Other methods - IsEqualObject()
        bool bSame = sp2.IsEqualObject( sp1 );
    
        // Other methods - CopyTo() - calls AddRef()
        IObject1 *pOb2;
        sp2.CopyTo( &pOb2 ); // AddRef() called because a copy is make

        // .. Use pOb2
        pOb2->Release(); // release it because it's not a smart pointer.

    } // smart pointer release
}

 
_com_ptr_t<>

Note that in methods 2 and 3, CoCreateInstance() is called, rather then  CoCreateInstanceEx(). Remote objects must be created with method 1 only.

// Used to create IObject1 with CoCreateInstance()
struct __declspec(uuid("aeca53e3-f3a6-11d3-8eb4-000000000000")) Object1;
struct __declspec(uuid("9d5ace00-f934-11d3-8ebc-000000000000")) IEngine : IDispatch
{ };
struct __declspec(uuid("aeca53e2-f3a6-11d3-8eb4-000000000000")) IObject1 : IDispatch
{
    virtual HRESULT __stdcall get_Number ( long * pVal ) = 0;
    virtual HRESULT __stdcall put_Number ( long pVal ) = 0;
    virtual HRESULT __stdcall raw_SomeFunc ( long nNumber ) = 0;
    virtual HRESULT __stdcall raw_GetEngine ( struct IEngine ** ppEngine ) = 0;
};

// Used to create IObject1 with smart pointers
//#import "Generic.tlb" no_namespace 


void CreateObjectComPtrT()
{
    /* Create an object using CoCreateInstance() and attach it to a smart
    pointer. Note how _com_ptr_t<> needs to take _com_IIID<> as its template
    argument. Also note how _com_IIID<> takes the address of the interface IID */

    {
        HRESULT hr;
        IObject1 *pItf = NULL;

        // Create an object
        hr = CoCreateInstance
            ( 
            __uuidof(Object1), // aeca53e3-f3a6-11d3-8eb4-000000000000
            NULL,
            CLSCTX_ALL,
            __uuidof( IObject1), // aeca53e2-f3a6-11d3-8eb4-000000000000
            reinterpret_cast<void**>(&pItf)
            );

        // Initialize smart pointer to NULL. 
        _com_ptr_t< _com_IIID<IObject1, & __uuidof(IObject1)> > sp1(NULL);
        sp1.Attach( pItf );

    } // smart pointer released here

    /* This segment illustrates creating an object using method 2*/
   
{
        // This takes care of creating IObject1Ptr. No need to use #import
        _COM_SMARTPTR_TYPEDEF(IObject1, __uuidof(IObject1));
        IObject1Ptr p1;
        p1.CreateInstance( __uuidof(Object1) );

        /* Assignment operator can be used to assign homogenous or heterogeneous
        pointers. In the code below, the assignment operator= will do a QI() for
        the required interface type (IObject1) if p2 was not of type IObject1 */

        IObject1Ptr p2;
        p2 = p1; // Calls AddRef() on p2 since we are making a copy
        // No need to call AddRef() ourselves

    } // smart pointers released here
}


Strings

PREREQUISITES
UNICODE

Conclusion: The marshaller layer must always know how many bytes to trasnsmit. The BSTR data type works exactly in this way: A BSTR is actually an OLECHAR* and can be treated as such when programming. However, when a BSTR is marshalled it is marshalled as a wireBSTR which is a BSTR and its associated length.

BSTR

// Forward declaration
BSTR TransformBSTR( BSTR bstrIn );

void DemoStrings()
{
    /* Example 1
    Most COM functions use LPOLESTR and OLECHAR for strings. COM functions 
    that return strings to the caller are allocated by the calle and freed 
    by the caller. COM functions that receive strings must be allocated by 
    caller and freedby caller.

    RULE - Functions taking a pointer to a pointer
    Passing a pointer to a pointer is the same as passing the address of the
    pointer. This is used when we wish to change the value of the pointer 
    stored inside the variable (which is of type pointer). Typical situation
    is passing a string pointer to have the called functions allocated a
    string. See below -

    StringFromCLSID takes an address of a pointer. Think of it as follows -
    lpString is a variable (a box) with uninitialized content and since it's a 
    pointer, the uninitialized content is an invalid memory address. The 
    variable's address it sent and we get back the variable containing a valid 
    memory address; a pointer to a string. 
    */
    LPOLESTR     lpString;
    const CLSID cls = {0xAECA53E3,0xF3A6,0x11D3,{0x8E,0xB4,0x00,0x00,0x00,0x00,0x00,0x00}};
    HRESULT hr = StringFromCLSID( cls, &lpString );
    CoTaskMemFree( lpString );

    // Defining literal strings
    WCHAR wsz[]  = OLESTR("going in");
    WCHAR wsz2[] = L"in in";

    /* Any function that creates/returns a BSTR must ensure that the length is 
    prefixed, else you can treate a BSTR as an 'const OLECHAR*' or LPCOLESTR 
    which mean we can read from it but not write to it. Note that SysAllocString()
    takes an OLECHAR* which use a NULL character to indicate end of string. To get
    around having a null in the string to be allocated, use SysAllocStringLen() */
    BSTR bstr         = SysAllocString( L"A BSTR \0 String");  // allocates "A BSTR "
    OLECHAR * oleStr1 = (LPOLESTR) bstr;               // Casting is not important
    OLECHAR * oleStr2 = bstr;                          // Casting can be ignored
    int nLen          = wcslen( oleStr1 );
    SysFreeString( bstr );

    /*Manipulating a BSTR: COPY into an LPOLESTR buffer, manipulate, then recopy
    into the BSTR again */

    // BSTR bstr1 = SysAllocString( "hello there"); // ERROR. Must have an OLESTR
    BSTR bstr1 = SysAllocString( L"hello there" ); // Get BSTR
    //
    OLECHAR *pStr = new OLECHAR[64];               // Copy
    wcscpy( pStr, bstr1);                          // Do not use: OLECHAR *pStr = bstr1;
    //
    wcscat( pStr, L"Mr. Diranieh");                // Manipulate
    //
    SysReAllocString( &bstr1, pStr);               // Re-assign
    //
    SysFreeString( bstr1 );                         // Free memory
    delete pStr;

    /* Using SysAllocStringLen() */
   
// bstr2 contains "Te". Char length = 2, Byte length = 4
    BSTR bstr2 = SysAllocStringLen( L"Text", 2);
    SysFreeString( bstr2 );

    // bstr3 contains "Text" followed by \0 and a a junk char - a total of 
    six char and then the EXTRA terminating null character.
    BSTR bstr3 = SysAllocStringLen( L"Text", 6); 
    SysFreeString( bstr3 );

    /* Rule 7 (see below) - Must create a BSTR in order to return it */
    BSTR bstr5 = TransformBSTR( bstr3 );
    SysFreeString( bstr5 );

    /* NULL and empty BSTRs. However, BOTH have a length of zero */
    BSTR bstr6 = SysAllocString( L"" );
    BSTR bstr7 = SysAllocString( NULL );
    int nLen6  = SysStringLen( bstr6 );
    int nLen7  = SysStringLen( bstr7 );
    SysFreeString( bstr6 );
}

// Rule 7 (see below) Must create a BSTR in order to return it (cannot return 
// the input param)
BSTR TransformBSTR( BSTR bstrIn )
{
    BSTR bstr = SysAllocString( bstrIn );
    return bstr;
}

EIGHT RULES OF BSTR
  1. Always use the Sys functions to allocate/destroy/measure BSTRs
  2. Since BSTRs may contain embedded NULLs, use SysStringLen() to determine the excat length ( _tcslen() on a BSTR will report only up to the first NULL character.)
  3. Since BSTR is an OLECHAR*, we can only change the pointer using SysReAllocString() and SysReAllocStringLen() functions.
  4. When passing a BSTR by value to a function, the CALLER owns the BSTR (in terms of destruction) -
  5. You own any BSTR passed to you by reference as an in/out parameter -
        void ModifyThidBSTR( BSTR *pBSTR );
  6. You must create any BSTR passed to you by reference as an out parameter
        void TakeNothingAndGiveMeAString( BSTR * pbstrOut );
  7. You must create a BSTR in order to return it . When a function takes a BSTR also return a BSTR, you cannot take the passed BSTR and return it. Instead create a new BSTR and return it

    BSTR TransformThisString( BSTR bstrIn )
    {
        BSTR bstr = SysAllocString( bstrIn );
        bstr.ToUpper();
        return bstr;
    }

     
  8. A null pointer is the same as an empty string to a BSTR. In C++ a NULL pointer is an invalid pointer and cannot obtain a length on it. A NULL BSTR and an empty BSTR both have the length of zero 

BSTR bstr6 = SysAllocString( L"" );
BSTR bstr7 = SysAllocString( NULL );
ASSRT( SysStringLen( bstr6 ) == SysStringLen( bstr7 ) ) // length = 0

_bstr_t

// Forward declaration
void PassBSTR( BSTR bstr);

void Demo_bstr_t()
{
    /* A _bstr_t constructor can take a UNICODE string. Normal form */
    _bstr_t bstr1( L"This is bstr1" );

    /* A _bstr_t constructor can take an ANSI string. Multibyte to UNICODE
    is performed before calling SysAllocString() */
   
_bstr_t bstr2( "This is bstr2" );
    
    /* A _bstr_t constructor can take a BSTR. The supplied BSTR can be attached
    (_bstr_t wraps it), or a totally new copy of the BSTR can be created 
    In both cases, SysFreeString() is managed by _bstr_t */
    BSTR bstr = SysAllocString( L"This is BSTR" );
    _bstr_t bstr3( bstr, false );
    _bstr_t bstr4( bstr, true );

    /* Illustrates ref count on _bstr_t 
    At end of 2nd line, both bstrs point to the same string. The copy constructor
    of the bstr6 increments the ref count. However, in the third line, the ref 
    count is decremented and a new BSTR is assigned. Use F11 to watch the _bstr_t 
    code for each of the steps below to verify the use of the ref count */
   
_bstr_t bstr5( L"This is bstr5" );
    _bstr_t bstr6 = bstr5;
    bstr5 = L"Still bstr5"; 

    /* To print the contents of a _bstr_t use operator char*() */
    printf("string is %s", (LPSTR)bstr4); // same as (char*)bstr5

    /* Using operator+=( const _bstr_t &). 
    Since we are supplying an OLESTR and operator+=() takes a 'const _bstr_t &', 
    we must create a temp object using _bstr_t::_bstr_t(const wchar_t *) conversion
    constructor. The temp object is later destroyed */
   
{
        _bstr_t bstr7 = L"This is";
        bstr7 += L"bstr7";
    }

    /* Passing _bstr_t to COM functions. Consider the following
        p->SetString( bstr6 ); 
        ...
        inline HRESULT IObject1::SetString ( _bstr_t bstrString ) 
        {
            HRESULT _hr = raw_SetString(bstrString);
        ..
        }
    First, the copy constructor is called since we're passing by value. SetString()
    expects a BSTR but we're passing a _bstr_t. This will cause operator wchar_t*()
    to be invoked in order to convert _bstr_t to BSTR */
   
IObject1Ptr p( __uuidof(Object1) );
    p->SetString( bstr6 );
    _bstr_t bstr8 = p->GetString();

    /* Use _bstr_t wherever BSTR is used */
   
// The following calls _bstr_t::_bstr_t(const wchar_t* s)
       
_bstr_t bstr9 = SysAllocString( L"This is a bstr" );
    // The following calls _bstr_t::operator wchar_t*()
        PassBSTR( bstr9 );
}
void PassBSTR( BSTR bstr)
{
    // Do something with with the bstr
    return;
}

 
CComBSTR

// Forward declaration
void PassSomeBSTR( BSTR bstr );

void Demo_CComBSTR()
{
    USES_CONVERSION;

    // CComBSTR() constructors handle UNICODE and ANSI strings
    CComBSTR bstr1( L"UNICODE String" );
    CComBSTR bstr2( "ANSI String" );     // Uses A2WBSTR() to convert ANSI to UNICODE

    // There is no operator+(). Use CComBSTR::Append()
    CComBSTR bstr3( L"CComBSTR3" );
    bstr3.Append( " With Append" );

    // AppendBSTR() is used to append only a BSTR
    CComBSTR bstr4( L"CComBSTR4" );
    BSTR bstr = SysAllocString( L" IS A BSTR" );
    bstr4.AppendBSTR( bstr );

    // Use CComBSTR anywhere a BSTR is used
    CComBSTR bstr5 = SysAllocString( L"BSTR5" ); // CComBSTR::CComBSTR( LPCOLESTR )
    PassSomeBSTR( bstr5 );                       // CComBSTR::CComBSTR operator BSTR()

    /* Using Attach/Detach() : Use Attach() if you have an existing BSTR and 
    you want to wrap with a CComBSTR. Use Detach() to retrieve the wrapped BSTR */
   
{
        CComBSTR bstr6;
        BSTR     bstrBSTR1 = SysAllocString( L"Another BSTR" );
        bstr6.Attach( bstrBSTR1 );
        BSTR bstrBSTR2 = bstr6.Detach();     // set bstr6.m_strto NULL
        SysFreeString( bstrBSTR2 );          // who else is gonna call it?
    } // bstr6 destructor calls SysFreeString(m_str) on a NULL m_str

    /* ATL conversion macros - Uses when we need to convert between ANSI and 
    UNICODE strings. */
    LPSTR lpszString     = "This is a T string";
    LPOLESTR lpoleString = A2OLE( lpszString );
    BSTR bstrString      = A2BSTR( lpszString ); // must call SysFreeString();
    SysFreeString( bstrString );
}

void PassSomeBSTR( BSTR bstr )
{
    CComBSTR bstr1;
    bstr1 = bstr; // CComBSTR& operator=(LPCOLESTR pSrc)
    return;
}

Variants

VARIANT

void DemoVariants()
{
    /* Using a variant - always initialize it first then clear it when finished */    
    VARIANT     var1;
    VariantInit( &var1 );            // Initialize
    var1.vt    = VT_INT;            
    var1.iVal  = 10;
    int nSize  = sizeof( var1 );     // size = 16 bytes. Parenthesis required.
    nSize      = sizeof VARIANT;     // size = 16 bytes. Parenthesis not required
    VariantClear( &var1 );

    /* For variants of type VT_BSTR, call VariantClear() to free the string */
    BSTR bstr = SysAllocString( L"My BSTR" );
    VARIANT var2;
    VariantInit( &var2 );
    var2.vt = VT_BSTR;               // or: V_VT( &var2 ) = VT_BSTR
    var2.bstrVal = bstr;             // or: V_BSTR( &var2 ) = bstr;
    VariantClear( &var2 );           // Calls SysFreeString()

    /* VT_BYREF is used to store pointers to the associated data type */
    BSTR bstr2 = SysAllocString( L"BSTR2" );
    VARIANT var3;
    VariantInit( &var3 );
    var3.vt       = VT_BSTR | VT_BYREF;
    var3.pbstrVal = &bstr2;
    VariantClear( &var3 );          // Calls SysFreeString()

    /* Changing variant types. Use the VarT1FromT2 functions. The following 
    retrieves the integer value into a long value */
    long     lVal;
    VARIANT  var4;
    VariantInit( &var4 ); 
    var4.vt     = VT_I2;
    var4.iVal   = 10;
    // Extract a long from a variant containing a short
    VarI4FromI2( var4.iVal, &lVal );     // lVal contains 10
    // Use a short to assign data to variable of type long
    lVal        = lVal + 100;
    VarI2FromI4( lVal, &var4.iVal );
    VariantClear( &var4 );

    /* Changing variant types. The following gets the system time and stores it
    in a variant. We then change the variant from type DATE to type BSTR */
    SYSTEMTIME    sysTime;
    double        date;
    VARIANT       var5;
    VariantInit( &var5 );
    //
    GetSystemTime( &sysTime );
    SystemTimeToVariantTime( &sysTime, &date );
    //
    var5.vt     = VT_DATE;
    var5.date   = date;
    HRESULT hr  = VariantChangeType( &var5, &var5, VARIANT_NOVALUEPROP, VT_BSTR);
    //
    VariantClear( &var5 );

    /* Accomplishing the same DATE to BSTR conversion using VarBstrFromDate */
    SYSTEMTIME    sysTime2;
    double        date2;
    VARIANT       var6;
    VariantInit( &var6 );
    //
    GetSystemTime( &sysTime2 );
    SystemTimeToVariantTime( &sysTime2, &date2 );
    //
    BSTR     bstrDate;
    LCID lcid = GetUserDefaultLCID();
    VarBstrFromDate( date2, lcid, 0, &bstrDate );
    //
    var6.vt      = VT_BSTR;
    var6.bstrVal = bstrDate;
    //
    VariantClear( &var6 );
}    

_variant_t

void Demo_variant_t()
{
    {
        // ANSI is converted to BSTR (A2BSTR)
        _variant_t vart1( "ANSI String"); 
    } // VariantClear(this) is called here

    {
        _bstr_t bstrVart2;
        _variant_t vart2( L"UNICODE String" );  // Calls SysAllocString() directly
        bstrVart2 = (_bstr_t) vart2;            // _variant_t::operator _bstr_t()
    }

    {
        /* This sequence show how to attach a variant, use it, and then detach
        it and clear it */
       
_variant_t  vart;
        //
        VARIANT var;
        VariantInit( &var );
        var.vt   = VT_I2;
        var.iVal = 10;
        //
        vart.Attach( var );     // VARIANT var is now wrapped by _variant_t
                                // VARIANT var is also cleared and set to VT_EMPTY
        // Start using the attached VARIANT
        vart.ChangeType( VT_I4 ); 
        //
        var = vart.Detach();  // Set var back to what it originally was 
        VariantClear( &var ); // Now clear 'var' since it's no longer wrapped
    }
}

 

CComVariant<>

void DemoCComVariant()
{
    /* Basic usage. It will convert the ANSI string to BSTR using ATL conversio
    macro - A2COLE() and assign vt = VT_BSTR.
    To access
    */
   
{
        CComVariant var1( "ANSI" );
        int nVT     = var1.vt;
        BSTR bstr   = var1.bstrVal;
    } // the bstr is freed when ~CComVariant is called.

    // Basic usage as above but with a UNICODE string
    {
         CComVariant var2( L"UNICODE" );
        BSTR bstr2 = var2.bstrVal;
    }

    /* Illustrating attaching/detaching a variant
    When attaching, the contents of the source (attached) variant are copied 
    into the target variant (the CComVaraint) and the source is set to VT_EMPTY.
    The opposite happens when detaching - CComVaraint contents are copied into 
    the VARIANT using memcpy(), and the CComVariant is set to VT_EMPTY. */

    {
        CComVariant var3; // Set to VT_EMPTY
        VARIANT var;
        // Create a variant
        VariantInit( &var );
        var.vt   = VT_I2;
        var.iVal = 10;
        // Attach variant and use it
        // contents of var are copied into var3 using memcpy(). var3 is set to 
        // VT_EMPTY
        var3.Attach( &var ); 
        int iValue = var3.iVal;
        // Detach variant and clear it
        // contents of var3 are copied into var using memcpy(). var is set
        // to VT_EMPTY
        var3.Detach( &var ); 
        VariantClear( &var );
    }

    /* CComVaraint::Copy copies the varinat using VariantCopy(). The original 
    VARIANT must still be freed since it was not attached */
   
{
        CComVariant var4; // Set to VT_EMPTY
        VARIANT var;
        // Create a variant
        VariantInit( &var );
        var.vt   = VT_I2;
        var.iVal = 10;
        // Copy the variant into a CComVariant
        var4.Copy( &var );
        // Clear VARIANT since it was copied NOT attached
        VariantClear( &var ); 
    }

    /* CComVariant::operator () functions */
   
{
        CComVariant var1;
        var1 = 20;

        CComVariant var2;
        var2 = SysAllocString( L"UNICODE" );

        CComVariant var3;
        var3 = CComBSTR( "ANSI" );

        // Incorrect way of converting BSTR to LPSTR. Use conversion macros
        BSTR bstr2 = SysAllocString( L"UNICODE" );
        char* lpString = (char*) bstr2;
    }
}

 

SafeArrays

void DemoSafeArray()
{
    /* Create a one dimensional safearray */
   
{
        // Set dimension information. Two dimensions
        SAFEARRAYBOUND saBound[1];
        saBound[0].cElements = 10;  // 10 elements in first dimension
        saBound[0].lLbound = 0;     // lower bound of 0

        // Create safearray
        SAFEARRAY *pSA;
        pSA = SafeArrayCreate( VT_I4,     // data type held in safearray is 'long'
                               1,         // 1 dimension
                               saBound ); // data about the only dimension

        // Access the safearray by retrieving a pointer to the data
        HRESULT hr;
        long *plData;
        hr = SafeArrayAccessData( pSA, (void**)&plData );

        // Use the data
        for (int i = 0; i < 10; i++)
        {
            plData[i] = i;
        }

        // UnAccess the safearray
        hr = SafeArrayUnaccessData( pSA );

        // Destroy safe array
        SafeArrayDestroy( pSA );
    }


    /* Create a two dimensional safearray */
   
{
        // Set dimension information. Two dimensions
        SAFEARRAYBOUND saBound[2];
        saBound[0].cElements = 5;     // 5 elements in first dimension
        saBound[0].lLbound = 0;       // lower bound of 0
        saBound[1].cElements = 5;     // 5 elements in second dimension
        saBound[1].lLbound = 0;       // lower bound of 0

        // Create safearray
        SAFEARRAY *pSA;
        pSA = SafeArrayCreate( VT_I4,     // data type held in safearray is 'long'
                               2,         // 2 dimensions
                               saBound ); // data about the 2 dimension

        // Use the data - first dimension
        long l2DData[5][5] = {
                {1,2,3,4,5},
                {6,7,8,9,10},
                {11,12,13,14,15},
                {16,17,18,19,20},
                {21,22,23,24,25} 
            };

        long ix[2];
        long lData;

        // Set / Get first row first column
        ix[0] = 0;
        ix[1] = 0;
        lData = l2DData[0][0];
        SafeArrayPutElement( pSA, ix, (void*)&lData );
        //
        lData = 0;
        SafeArrayGetElement( pSA, ix, &lData);

        // Set / Get fifth row third colums
        ix[0] = 4;
        ix[1] = 2;
        lData = l2DData[4][2];
        SafeArrayPutElement( pSA, ix, (void*)&lData );
        //
        lData = 0;
        SafeArrayGetElement( pSA, ix, &lData );

        // Another way to access a particular element in the SAFEARRAY. ix[] 
        // indicates the index of the data to be accessed. The data is copied 
        // to pData;
        long *pData;
        SafeArrayLock( pSA );
        SafeArrayPtrOfIndex( pSA, ix, (void**)&pData );
        lData = *pData;
        SafeArrayUnlock( pSA );

        // Get dimensions using SafeArrayX() functions
        long lBound, uBound;
        SafeArrayGetLBound( pSA, 1, &lBound );
        SafeArrayGetUBound( pSA, 1, &uBound );

        // Destroy safe array
        SafeArrayDestroy( pSA );
    }
}

 

Exceptions

HRESULT
RICH ERROR HANDLING
EXCEPTIONS

void DemoErrors()
{
    // Play with HRESULT
    HRESULT hr = 0x800706ba;                  // simulate an error
    long lFacility = HRESULT_FACILITY( hr );  // used to determine who made the error
    long lCode = HRESULT_CODE( hr );          // used to get err description
    if (FAILED(hr) ) // testing for failure.
    {
        hr = 0x00000000;
    }

    // TO DO: code to get an error object form an interface method.
}