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

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • Remember getting your first box of LEGOS as a kid? How fun it was putting the pieces together, collaborating with your friends to create something new? Now, as an IT professional, assembling and maintaining a Lego-like collaboration infrastructure isn't what you signed up for. Piecing together disparate systems of record for email, web meetings and other applications is about as painful as stepping on a pile of Legos. Download the e-book to learn how implementing a collaboration system connects systems of …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date