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

  • When storage performance issues are present, it impacts several resources attached to the storage system. However, often it's not a disk capacity issue, but a storage I/O bottleneck problem that can occur in a storage environment because of the unbalanced load placed on the storage systems by the application environment. This white paper discusses the top four reasons why storage I/O bottlenecks occur and how it impacts the overall storage performance, along with some tips and best practices to achieve optimum …

  • On-demand Event Event Date: May 18, 2015 While the idea of using facial and/or gesture recognition to create a modern, intuitive game seems attractive, some developers may want to leverage Unity 3D as a way to accelerate their development efforts. There are many different ways in which Intel and Unity Technologies have been working together to help speed the development of games with the Intel&reag; RealSense™ SDK (software developer kit). Check out this webcast to join a panel of experts as they …

Most Popular Programming Stories

More for Developers

RSS Feeds

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