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

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

  • 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 …

Most Popular Programming Stories

More for Developers

RSS Feeds