Wednesday, August 12, 2009

Creating an IStream or ISequentialStream From a String for XmlLite

XmlLite needs an IStream or an ISequentialStream to parse from. You can get one by opening a file like I showed in the previous post, but in my real code I didn’t have a file, I had a string. No biggie, you can always implement your own if there isn’t one already. This CStringStream class implements ISequentialStream using a string as an input. The class factory method takes in a string, creates a buffer, and gives back an ISequentialStream. Awesome, just what you need if you want to use XmlLite on XML in a string. Here is the class implementation:

#pragma once

//
// this class creates an ISequentialStream from a string
//
class CStringStream : public ISequentialStream
{
public:
// factory method
__checkReturn static HRESULT Create(
__in LPWSTR psBuffer,
__deref_out ISequentialStream **ppStream)
{
HRESULT hr = S_OK;
void *pNewBuff = NULL;
size_t buffSize = 0;

if (!psBuffer)
{
return E_INVALIDARG;
}

*ppStream = NULL;

buffSize = (wcslen(psBuffer)+1) * sizeof(wchar_t);
pNewBuff = malloc(buffSize);

if (!pNewBuff)
{
return E_OUTOFMEMORY;
}

hr = StringCbCopy((LPWSTR)pNewBuff, buffSize, psBuffer);

if (S_OK == hr)
{
*ppStream = new CStringStream(
buffSize,
pNewBuff);
}

if (!*ppStream)
{
hr = E_FAIL;
}

return hr;
};

// ISequentialStream
__checkReturn HRESULT STDMETHODCALLTYPE Read(
__out_bcount_part(cb, *pcbRead) void *pv,
/* [in] */ ULONG cb,
__out_opt ULONG *pcbRead)
{
HRESULT hr = S_OK;

for (*pcbRead = 0; *pcbRead < cb; ++*pcbRead, ++m_buffSeekIndex)
{
// we are seeking past the end of the buffer
if (m_buffSeekIndex == m_buffSize)
{
hr = S_FALSE;
break;
}

((BYTE*)pv)[*pcbRead] = ((BYTE*)m_pBuffer)[m_buffSeekIndex];
}

return hr;
};

HRESULT STDMETHODCALLTYPE Write(
__in_bcount(cb) const void *pv,
/* [in] */ ULONG cb,
__out_opt ULONG *pcbWritten)
{
return E_NOTIMPL;
};

// IUnknown
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
};

STDMETHODIMP_(ULONG) Release()
{
LONG cRef = InterlockedDecrement(&m_cRef);

if (0 == cRef)
{
delete this;
}

return cRef;
};

STDMETHODIMP QueryInterface(REFIID riid, __deref_out_opt void **ppv)
{
HRESULT hr = S_OK;

if (ppv)
{
*ppv = NULL;
}
else
{
hr = E_INVALIDARG;
}

if (S_OK == hr)
{
if ((__uuidof(IUnknown) == riid) || (riid == __uuidof(ISequentialStream)))
{
AddRef();
*ppv = (ISequentialStream*)this;
}
else
{
hr = E_NOINTERFACE;
}
}

return hr;
};

protected:
LONG m_cRef;
void *m_pBuffer;
size_t m_buffSize;
size_t m_buffSeekIndex;

// constructor/deconstructor
CStringStream(
__in size_t buffSize,
__in void *pBuff)
:
m_cRef(1),
m_pBuffer(pBuff),
m_buffSize(buffSize),
m_buffSeekIndex(0)
{
};

~CStringStream()
{
free(m_pBuffer);
};
};

3 comments:

  1. can you post your xmllite code which actually uses the CStringStream to read XML from a string and write XML to a string. thanks. this is useful.

    ReplyDelete
  2. I will try to dig it up, and sanitize it for the internet.

    ReplyDelete
  3. http://samscode.blogspot.com/2009/10/how-to-use-string-stream-implementation.html

    This is where you can see how to use it with xml lite.

    ReplyDelete