Figure 1   VARIANT.cpp

 typedef struct tagVARIANT{
    VARTYPE vt;
    WORD wReserved1;
    WORD wReserved2;
    WORD wReserved3;
    union {
      long          lVal;           /* VT_I4                */
      unsigned char bVal;           /* VT_UI1               */
      short         iVal;           /* VT_I2                */
      float         fltVal;         /* VT_R4                */
      double        dblVal;         /* VT_R8                */
      VARIANT_BOOL  boolVal;        /* VT_BOOL              */
      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|*           */
      unsigned char *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  *pbool;         /* 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  */
      void          * byref;        /* Generic ByRef        */
    };
} VARIANT, VARIANTARG;

Figure 5   PassARect

 void PassARect(INeedARectAlso *pnar, const RECT& r) 
{
// create a safearray descriptor, using the actual
// RECT as the byte array, avoiding redundant 
// memory copying
  SAFEARRAY sa = {
    1,                       // 1-dimensional
    FADF_AUTO|FADF_FIXEDSIZE,// my memory
    sizeof(char),            // elems are 1 byte
    0,                       // no locks
    (void*)&r,               // here's the array!
    {
      sizeof(RECT),          // it has 16 elems
      0,                     // ALL arrays start at 0
    }
  }
// create a VARIANT that contains our safearray
  VARIANT arg; ZeroMemory(&arg, sizeof(arg));
  arg.vt = VT_UI1 | VT_ARRAY;
  arg.parray = &sa;
// call the function
  pnar->GiveMeARect(arg);

}

Figure 7   Serializing Each Struct Member into an Array of VARIANTs

 void PassARect(INeedARectAsWell *pnar, const RECT& r) 
{
// create and initialize an array of variants 
// that will hold each element of our struct
  VARIANT vars[4]; ZeroMemory(vars, sizeof(vars));
  vars[0].vt = VT_I4; vars[0].lVal = r.left;
  vars[1].vt = VT_I4; vars[1].lVal = r.top;
  vars[2].vt = VT_I4; vars[2].lVal = r.right;
  vars[3].vt = VT_I4; vars[3].lVal = r.bottom;
  
// create a safearray descriptor, using the array
// above
  SAFEARRAY sa = {
    1,                       // 1-dimensional
    FADF_VARIANT|FADF_AUTO|FADF_FIXEDSIZE,// my memory
    sizeof(VARIANT),         // elems are 16 bytes
    0,                       // no locks
    (void*)vars,             // here's the array!
    {
      4,                     // it has 4 elems
      0,                     // ALL arrays start at 0
    }
  }
// create a VARIANT that contains our safearray
  VARIANT arg; ZeroMemory(&arg, sizeof(arg));
  arg.vt = VT_VARIANT | VT_ARRAY;
  arg.parray = &sa;
// call the function
  pnar->GiveMeARect(arg);
}

Figure 8   Mapping the RECT Structure Onto a Dual Interface

 [ 
    uuid(7A52A801-860B-11cf-B1D2-0080C7BC7884),
    odl, dual
]
interface IRect : IDispatch
{
    [propget, id(1)] 
HRESULT Left([out, retval] long *pVal); [propput, id(1)]
HRESULT Left([in] long val); [propget, id(2)]
HRESULT Top([out, retval] long *pVal); [propput, id(2)]
HRESULT Top([in] long val); [propget, id(3)]
HRESULT Right([out, retval] long *pVal); [propput, id(3)] HRESULT Right([in] long val); [propget, id(4)] HRESULT Bottom([out, retval] long *pVal); [propput, id(4)] HRESULT Bottom([in] long val); }

Figure 9   Dual Interface Wrapper for RECT

StructLib.od

 ///////////////////////////////////////////////////////////
//
// StructLib.odl - an implementation of a COM wrapper
//                 to a RECT that exposes the embedded
//                 struct via dual/dispatch properties

[
    uuid(7A52A800-860B-11cf-B1D2-0080C7BC7884),
    lcid(9),
    version(1.0),
    helpstring("A library desparately in need of structure support!")
]
library StructLib
{
    importlib("stdole32.tlb");

    [ 
        uuid(7A52A801-860B-11cf-B1D2-0080C7BC7884),
        odl,
        dual,
        oleautomation
    ]
    interface IRect : IDispatch
    {
        [propget, id(1)] HRESULT Left([out, retval] long *pVal);
        [propput, id(1)] HRESULT Left([in] long val);
        [propget, id(2)] HRESULT Top([out, retval] long *pVal);
        [propput, id(2)] HRESULT Top([in] long val);
        [propget, id(3)] HRESULT Right([out, retval] long *pVal);
        [propput, id(3)] HRESULT Right([in] long val);
        [propget, id(4)] HRESULT Bottom([out, retval] long *pVal);
        [propput, id(4)] HRESULT Bottom([in] long val);
    }        

    [
        uuid(7A52A803-860B-11cf-B1D2-0080C7BC7884)
    ]
    coclass CoRect
    {
        [default] interface IRect;
    }
}

CoRect.H

 ///////////////////////////////////////////////////////////
//
// CoRect.h - an implementation of a COM wrapper
//              to a RECT that exposes the embedded
//              struct via dual/dispatch properties

#ifndef _COSTRUCTS_H
#define _COSTRUCTS_H

#include "StructLib.h"

class CoRect : public IRect
{
// COM stuff
    LONG        m_cRef;
    ITypeInfo  *m_pTypeInfo;
// Domain-specific stuff
    RECT        m_rect;

// helper function to get safely access our type description
    ITypeInfo *GetTypeInfo(HRESULT &hr);
public:
    CoRect();
    virtual ~CoRect();

// IUnknown methods
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

// IDispatch methods
    STDMETHODIMP GetTypeInfoCount(unsigned int * pctInfo);
    STDMETHODIMP GetTypeInfo(unsigned int nInfo, LCID lcid, 
                             ITypeInfo **ppTypeInfo);
    STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames,
                               unsigned int cNames, LCID lcid, 
                               DISPID * rgdispids);
    STDMETHODIMP Invoke(DISPID dispid, REFIID riid, LCID lcid,
                        WORD wFlags, DISPPARAMS *pdispparams,
                        VARIANT *pVarResult, EXCEPINFO *pei,
                        unsigned int *puArgError);

// IRect methods
    STDMETHODIMP get_Top(long *pVal);
    STDMETHODIMP put_Top(long val);
    STDMETHODIMP get_Left(long *pVal);
    STDMETHODIMP put_Left(long val);
    STDMETHODIMP get_Bottom(long *pVal);
    STDMETHODIMP put_Bottom(long val);
    STDMETHODIMP get_Right(long *pVal);
    STDMETHODIMP put_Right(long val);
};

#endif

CoRect.cpp

 ///////////////////////////////////////////////////////////
//
// CoRect.cpp - an implementation of a COM wrapper
//              to a RECT that exposes the embedded
//              struct via dual/dispatch properties

#include <windows.h>
#include <TCHAR.h>
#include <initguid.h>
#include "CoRect.h"

void SvcLock(void);    // standard server lock/unlock functions 
void SvcUnlock(void);  // that hold/release server in memory

CoRect::CoRect()
: m_cRef(0), m_pTypeInfo(0)
{
    SvcLock();
    ZeroMemory(&m_rect, sizeof(m_rect));
}

CoRect::~CoRect()
{
    if (m_pTypeInfo)
        m_pTypeInfo->Release();
    SvcUnlock();
}

ITypeInfo *CoRect::GetTypeInfo(HRESULT &hr)
{
    if (!m_pTypeInfo)
    {
        ITypeLib *pTypeLib = 0;
        hr = LoadRegTypeLib(LIBID_StructLib, 1, 0, 9, &pTypeLib);
        if (FAILED(hr))
            hr = LoadTypeLib(OLESTR("StructLib.tlb"), &pTypeLib);
        if (SUCCEEDED(result))
        {
            hr = pTypeLib->GetTypeInfoOfGuid(IID_IRect, m_pTypeInfo);
            pTypeLib->Release();
        }
        else
            m_pTypeInfo = 0;
    }
    else
        hr = S_OK;
    return m_pTypeInfo;
}

// IUnknown methods
STDMETHODIMP CoRect::QueryInterface(REFIID riid, void **ppv)
{
    if (riid == IID_IRect || riid == IID_IDispatch || riid == IID_IUnknown)
        *ppv = (IRect*)this;
    else
        *ppv = 0;
    if (*ppv)
        ((IUnknown*)*ppv)->AddRef();
    return *ppv ? S_OK : E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CoRect::AddRef()
{
    InterlockedIncrement(&m_cRef);
    return m_cRef;
}

STDMETHODIMP_(ULONG) CoRect::Release()
{
    if (InterlockedDecrement(&m_cRef) != 0)
        return m_cRef;
    delete this;
    return 0;
}

// IDispatch methods
STDMETHODIMP CoRect::GetTypeInfoCount(unsigned int * pctInfo)
{
    HRESULT hr = S_OK;
    *pctInfo = (GetTypeInfo(hr) == 0) ? 0 : 1;
    return hr;
}

STDMETHODIMP CoRect::GetTypeInfo(unsigned int nInfo, LCID lcid, 
                                 ITypeInfo **ppTypeInfo)
{
    HRESULT hr = S_OK;
    if (*ppTypeInfo = GetTypeInfo(hr))
        (*ppTypeInfo)->AddRef();
    return hr;
}

STDMETHODIMP CoRect::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames,
                                   unsigned int cNames, LCID lcid, 
                                   DISPID * rgdispids)
{
    HRESULT hr = S_OK;
    ITypeInfo *pti = GetTypeInfo(hr);
    if (pti)
        hr = pti->GetIDsOfNames(rgszNames, cNames, rgdispids);
    return hr;
}

STDMETHODIMP CoRect::Invoke(DISPID dispid, REFIID riid, LCID lcid,
                            WORD wFlags, DISPPARAMS *pdispparams,
                            VARIANT *pVarResult, EXCEPINFO *pei,
                            unsigned int *puArgError)
{
    HRESULT hr = S_OK;
    ITypeInfo *pti = GetTypeInfo(hr);
    if (pti)
        hr = pti->Invoke((IRect*)this, dispid, wFlags, pdispparams,
        pVarResult,  pei, puArgError);
    return hr;
}

// IRect methods
STDMETHODIMP CoRect::get_Top(long *pVal)
{
    *pVal = m_rect.top;
    return S_OK;
}

STDMETHODIMP CoRect::put_Top(long val)
{
    m_rect.top = val;
    return S_OK;
}

STDMETHODIMP CoRect::get_Left(long *pVal)
{
    *pVal = m_rect.left;
    return S_OK;
}

STDMETHODIMP CoRect::put_Left(long val)
{
    m_rect.left = val;
    return S_OK;
}

STDMETHODIMP CoRect::get_Bottom(long *pVal)
{
    *pVal = m_rect.bottom;
    return S_OK;
}

STDMETHODIMP CoRect::put_Bottom(long val)
{
    m_rect.bottom = val;
    return S_OK;
}

STDMETHODIMP CoRect::get_Right(long *pVal)
{
    *pVal = m_rect.right;
    return S_OK;
}

STDMETHODIMP CoRect::put_Right(long val)
{
    m_rect.right = val;
    return S_OK;
}

Figure 10   Creating a Rect Wrapper

 void PassARect(INeedARectSoBadItHurts *pnar, const RECT& r) 
{
// create and initialize an wrapper object
// that will hold each element of our struct
  IRect *pRect;
  HRESULT hr =  CoCreateInstance(CLSID_CoRect, 0, CLSCTX_ALL, IID_IRect, 
                                 (void**)&pRect);
  
  if (FAILED(hr))
    throw "Couldn't create RECT wrapper";
  pRect->put_Left(r.left);
  pRect->put_Top(r.top);
  pRect->put_Right(r.right);
  pRect->put_Bottom(r.bottom);

// call the function
  pnar->GiveMeARect(pRect);
// release our wrapper
  pRect->Release();
}

Figure 13   name

Pass structure members as individual parameters

Pass structure as SAFEARRAY of bytes

Pass structure as SAFEARRAY of Variants

Pass structure inside dual interface wrapper object

Platform Independent

Yes

No

Yes

Yes

Supports structs with interface pointer members

Yes

No

Yes

Yes

Ease of use from Visual Basic

Great if few members

Difficult

Good

Good

Ease of use from C++

Great if few members

Good, provided no members are pointers

Fair

Good

Type-safety

Good, provided struct members are VARIANT-compatible

None

None

Good, provided struct members are VARIANT-compatible

Calls per Second:

C++ Client/In-process Object

3,215,434

1,386,963

900,090

15,504

Visual Basic 4.0 Client/In-process Object

204,082

55,556

22,173

5,546

C++ Client/Out-of-Process Object

736

700

681

41

Visual Basic 4.0 Client/Out-of-Process Object

674

648

618

33

All performance numbers based on passing a RECT from client to object. Higher numbers are better.

Results shown are for Windows NTÒ 4.0 Beta 1, 120 MHz Pentium, 40MB physical memory.