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;
}



Comments

  • Well Done !!

    Posted by Legacy on 03/19/2003 12:00am

    Originally posted by: Matthias Biel

    Thank you for this piece of code. I'm just wondering why these clever Microsoft guys didn't implement it in first place.
    M$ <=> pain in the ass.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: September 16, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you starting an on-premise-to-cloud data migration project? Have you thought about how much space you might need for your online platform or how to handle data that might be related to users who no longer exist? If these questions or any other concerns have been plaguing you about your migration project, check out this eSeminar. Join our speakers Betsy Bilhorn, VP, Product Management at Scribe, Mike Virnig, PowerSucess Manager and Michele …

  • Learn how cloud-based master data management (MDM) empowers your fast-paced business to get the right data to the right place in real time, so you can remain competitive and agile.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds