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   Tradeoffs

  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-saafety 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.