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