Handling the Structured Storage of SAFEARRAYs

An ATL-derived class to handle the structured storage of SAFEARRAYs
(CComVariantEx).

.

Environment: VC6 (ATL), NT4/2000/XP

Currently, the ATL CComVariant object can store itself to a “Structured Storage” file via the functions CComVariant::WriteToFile() and CComVariant::ReadFromFile(). However, this does not support CComVariant objects that contain SAFEARRAYs. Below is a derived CComVariantEx class in which these functions have been extended to support CComVariants that contain one-dimensional SAFEARRAYs of VT_VARIANT.

Feel free to extend this class to support multidimensional SAFEARRAYs.


// update to CComVariant to store SAFEARRAYs to the stream
class CComVariantEx : public CComVariant
{
public:

HRESULT WriteToStream(IStream* pStream);
HRESULT ReadFromStream(IStream* pStream);

};

inline HRESULT CComVariantEx::WriteToStream(IStream* pStream)
{
HRESULT hr(E_FAIL);

// enhancement to stream SAFEARRAYs containing VARIANTs

if((vt&VT_TYPEMASK)==vt)
{
hr = CComVariant::WriteToStream(pStream);
}
else if((VT_ARRAY|VT_VARIANT)==vt)
{
hr = pStream->Write(&vt, sizeof(VARTYPE), NULL);

if( SUCCEEDED(hr) )
{
CComVariantEx* pvData = NULL;

hr = SafeArrayAccessData(parray, (void HUGEP**)&pvData);

if( SUCCEEDED(hr) )
{
// only support 1-dimensional arrays
if( 1==SafeArrayGetDim(parray) )
{
hr = E_FAIL;

SAFEARRAYBOUND bnds;

long bnd(0);
HRESULT hr1 = SafeArrayGetLBound(parray,1,&bnd);
bnds.lLbound = bnd;
HRESULT hr2 = SafeArrayGetUBound(parray,1,&bnd);
bnds.cElements = 1 + bnd – bnds.lLbound;

if( SUCCEEDED(hr1) && SUCCEEDED(hr2) )
{
// write the SAFEARRAYBOUND information to the stream
hr = pStream->Write(&bnds,
sizeof(SAFEARRAYBOUND),
NULL);

// write all the variants in the SAFEARRAY to the stream
for(DWORD i=0;i&ltbnds.cElements && SUCCEEDED(hr);i++)
{
// write element to the stream
hr = pvData[i].WriteToStream(pStream);
}
}
}

SafeArrayUnaccessData(parray);
}
}
}
else if( vt==VT_NULL||vt==VT_EMPTY )
{
// write the type and nothing else following it
hr = pStream->Write(&vt, sizeof(VARTYPE), NULL);
}

return hr;
}

inline HRESULT CComVariantEx::ReadFromStream(IStream* pStream)
{
ATLASSERT(pStream != NULL);
HRESULT hr(E_FAIL);

hr = VariantClear(this);

VARTYPE vtRead(VT_NULL);

if(SUCCEEDED(hr))
{
DWORD dwBytesRead(0);
hr = pStream->Read(&vtRead, sizeof(VARTYPE), &dwBytesRead);
if (hr == S_FALSE || dwBytesRead==0)
hr = E_FAIL;
}

if (FAILED(hr))
return hr;

if((vtRead&VT_TYPEMASK)==vtRead)
{
LARGE_INTEGER llMove;
llMove.QuadPart = -(LONGLONG)sizeof(VARTYPE);

// need to reset to just before the VARTYPE bytes
// before calling ReadToStream

hr = pStream->Seek(llMove,STREAM_SEEK_CUR,NULL);

if( SUCCEEDED(hr) )
{
hr = CComVariant::ReadFromStream(pStream);
}
}
else if((VT_ARRAY|VT_VARIANT)==vtRead)
{
// get the bounds of the one dimensional safearray
// from the stream

SAFEARRAYBOUND bnds;
hr = pStream->Read(&bnds, sizeof(SAFEARRAYBOUND), NULL);

if( SUCCEEDED(hr) )
{
hr = E_FAIL;

// create a new SAFEARRAY to hold the streamed data
vt = VT_ARRAY|VT_VARIANT;
parray = SafeArrayCreateEx(VT_VARIANT,1,&bnds,NULL);

if( parray )
{
CComVariantEx* pvData=NULL;
hr = SafeArrayAccessData( parray,
(void HUGEP**) &pvData);

if( SUCCEEDED(hr) )
{
// read all the variants in the stream to
// the SAFEARRAY

for(DWORD i=0;i&ltbnds.cElements && SUCCEEDED(hr);i++)
{
// Read element from the stream
hr = pvData[i].ReadFromStream(pStream);
}

SafeArrayUnaccessData(parray);
}
}
}
}
else if( vtRead==VT_NULL||vtRead==VT_EMPTY )
{
// read the type and nothing else following it
vt = vtRead;
}

return hr;
}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read