Handling the Structured Storage of SAFEARRAYs
Posted
by Andy Smith
on July 31st, 2002
.
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<bnds.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<bnds.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; }

Comments
There are no comments yet. Be the first to comment!