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 …

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

Most Popular Programming Stories

More for Developers

RSS Feeds