windows-nt/Source/XPSP1/NT/shell/shdocvw/strmwrap.cpp
2020-09-26 16:20:57 +08:00

303 lines
7.8 KiB
C++

#include "priv.h"
#define MAX_STREAMS 5
#define CP_UNICODE 1200
class CStreamWrap : public IStream
{
public:
// *** IUnknown methods ***
STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **ppv);
STDMETHOD_(ULONG,AddRef) (THIS);
STDMETHOD_(ULONG,Release) (THIS);
// *** IStream methods ***
STDMETHOD(Read) (THIS_ void *pv, ULONG cb, ULONG *pcbRead);
STDMETHOD(Write) (THIS_ VOID const *pv, ULONG cb, ULONG *pcbWritten);
STDMETHOD(Seek) (THIS_ LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
STDMETHOD(SetSize) (THIS_ ULARGE_INTEGER libNewSize);
STDMETHOD(CopyTo) (THIS_ IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten);
STDMETHOD(Commit) (THIS_ DWORD grfCommitFlags);
STDMETHOD(Revert) (THIS);
STDMETHOD(LockRegion) (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
STDMETHOD(UnlockRegion) (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
STDMETHOD(Stat) (THIS_ STATSTG *pstatstg, DWORD grfStatFlag);
STDMETHOD(Clone)(THIS_ IStream **ppstm);
HRESULT Init(IStream *aStreams[], UINT cStreams, UINT uiCodePage);
CStreamWrap();
private:
~CStreamWrap();
LONG _cRef;
IStream *_aStreams[MAX_STREAMS];
BOOL _fFirstReadForStream[MAX_STREAMS];
UINT _cStreams;
UINT _iCurStream;
UINT _uiCodePage;
UINT _uiBOM; // Byte order marker
};
CStreamWrap::CStreamWrap() : _cRef(1)
{
}
CStreamWrap::~CStreamWrap()
{
while (_cStreams--)
{
if (_aStreams[_cStreams])
{
_aStreams[_cStreams]->Release();
_aStreams[_cStreams] = NULL;
}
}
}
HRESULT CStreamWrap::Init(IStream *aStreams[], UINT cStreams, UINT uiCodePage)
{
if (cStreams > ARRAYSIZE(_aStreams))
return E_FAIL;
for (_cStreams = 0; _cStreams < cStreams; _cStreams++)
{
_aStreams[_cStreams] = aStreams[_cStreams];
_fFirstReadForStream[_cStreams] = TRUE;
_aStreams[_cStreams]->AddRef();
}
_uiCodePage = uiCodePage;
_uiBOM = 0xfeff; // FEATURE - set default to byte order of machine
return S_OK;
}
STDMETHODIMP CStreamWrap::QueryInterface(REFIID riid, void **ppv)
{
if (IsEqualIID(riid, IID_IStream) || IsEqualIID(riid, IID_IUnknown))
{
*ppv = SAFECAST(this, IStream *);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
this->AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG) CStreamWrap::AddRef()
{
return InterlockedIncrement(&this->_cRef);
}
STDMETHODIMP_(ULONG) CStreamWrap::Release()
{
if (InterlockedDecrement(&this->_cRef))
return this->_cRef;
delete this;
return 0;
}
// Byte order marker macros
#define IS_BOM_LITTLE_ENDIAN(pv) ((*(WORD*)pv) == 0xfffe)
#define IS_BOM_BIG_ENDIAN(pv) ((*(WORD*)pv) == 0xfeff)
STDMETHODIMP CStreamWrap::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
ULONG cbReadTotal = 0;
ULONG cbLeftToRead = cb;
HRESULT hres = NOERROR;
while (cbLeftToRead && (_iCurStream < _cStreams))
{
ULONG cbReadThisStream;
hres = _aStreams[_iCurStream]->Read(pv, cbLeftToRead, &cbReadThisStream);
// REVIEW: what if one stream's implementation returns a failure code
// when reading at the end of the stream? We bail prematurely.
if (SUCCEEDED(hres))
{
cbLeftToRead -= cbReadThisStream;
if(_uiCodePage == CP_UNICODE)
{
if((_fFirstReadForStream[_iCurStream]) &&
(cbReadThisStream >= 2) &&
((IS_BOM_LITTLE_ENDIAN(pv)) || (IS_BOM_BIG_ENDIAN(pv)))
)
{
if(_iCurStream == 0)
{
_uiBOM = (*(WORD*)pv); // Save first streams byte order marker as default
}
else
{
// REVIEW: should handle swapping bytes to default for IE6
if(_uiBOM != (*(WORD*)pv)) // BOM not default
return(E_FAIL);
// Skip past unicode document lead bytes
cbReadThisStream -= 2;
MoveMemory((BYTE*)pv, (BYTE*)pv+2, cbReadThisStream);
}
}
_fFirstReadForStream[_iCurStream] = FALSE;
}
cbReadTotal += cbReadThisStream;
pv = (char *)pv + cbReadThisStream;
if (cbLeftToRead)
{
_iCurStream++;
hres = S_OK;
}
}
else
break;
}
if (pcbRead)
*pcbRead = cbReadTotal;
if (SUCCEEDED(hres) && cbLeftToRead)
hres = S_FALSE; // still success! but not completely
return hres;
}
STDMETHODIMP CStreamWrap::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
{
if (pcbWritten)
*pcbWritten = 0;
return E_NOTIMPL;
}
// FEATURE: could at least support seaking to 0, as that's a common thing to do.
// REVIEW: not too hard to implement thoroughly - cache Stat calls on each
// substream (help implement ::Stat in this file too, which IMO is needed.)
STDMETHODIMP CStreamWrap::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
return E_NOTIMPL;
}
STDMETHODIMP CStreamWrap::SetSize(ULARGE_INTEGER libNewSize)
{
return E_NOTIMPL;
}
//
// REVIEW: this could use the internal buffer in the stream to avoid
// extra buffer copies.
//
STDMETHODIMP CStreamWrap::CopyTo(IStream *pstmTo, ULARGE_INTEGER cb,
ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{
BYTE buf[512];
ULONG cbRead;
HRESULT hres = NOERROR;
if (pcbRead)
{
pcbRead->LowPart = 0;
pcbRead->HighPart = 0;
}
if (pcbWritten)
{
pcbWritten->LowPart = 0;
pcbWritten->HighPart = 0;
}
ASSERT(cb.HighPart == 0);
while (cb.LowPart)
{
hres = this->Read(buf, min(cb.LowPart, SIZEOF(buf)), &cbRead);
if (FAILED(hres) || (cbRead == 0))
break;
if (pcbRead)
pcbRead->LowPart += cbRead;
cb.LowPart -= cbRead;
hres = pstmTo->Write(buf, cbRead, &cbRead);
if (pcbWritten)
pcbWritten->LowPart += cbRead;
if (FAILED(hres) || (cbRead == 0))
break;
}
return hres;
}
STDMETHODIMP CStreamWrap::Commit(DWORD grfCommitFlags)
{
return NOERROR;
}
STDMETHODIMP CStreamWrap::Revert()
{
return E_NOTIMPL;
}
STDMETHODIMP CStreamWrap::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
return E_NOTIMPL;
}
STDMETHODIMP CStreamWrap::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
return E_NOTIMPL;
}
// FEATURE: you gotta support Stat, or Trident will barf on this stream.
// Trivial to implement too, just call Stat on each sub-stream.
STDMETHODIMP CStreamWrap::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
{
return E_NOTIMPL;
}
// REVIEW: so simple to implement, it's probably worth doing
STDMETHODIMP CStreamWrap::Clone(IStream **ppstm)
{
return E_NOTIMPL;
}
// in:
// ppstm array of stream pointers
// cStreams number of streams in the array
//
SHDOCAPI SHCreateStreamWrapperCP(IStream *aStreams[], UINT cStreams, DWORD grfMode, UINT uiCodePage, IStream **ppstm)
{
HRESULT hres;
*ppstm = NULL;
if (grfMode != STGM_READ)
return E_INVALIDARG;
CStreamWrap *pwrap = new CStreamWrap();
if (pwrap)
{
hres = pwrap->Init(aStreams, cStreams, uiCodePage);
if (SUCCEEDED(hres))
pwrap->QueryInterface(IID_IStream, (void **)ppstm);
pwrap->Release();
}
else
hres = E_OUTOFMEMORY;
return hres;
}