Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
7.8 KiB

  1. #include "priv.h"
  2. #define MAX_STREAMS 5
  3. #define CP_UNICODE 1200
  4. class CStreamWrap : public IStream
  5. {
  6. public:
  7. // *** IUnknown methods ***
  8. STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **ppv);
  9. STDMETHOD_(ULONG,AddRef) (THIS);
  10. STDMETHOD_(ULONG,Release) (THIS);
  11. // *** IStream methods ***
  12. STDMETHOD(Read) (THIS_ void *pv, ULONG cb, ULONG *pcbRead);
  13. STDMETHOD(Write) (THIS_ VOID const *pv, ULONG cb, ULONG *pcbWritten);
  14. STDMETHOD(Seek) (THIS_ LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
  15. STDMETHOD(SetSize) (THIS_ ULARGE_INTEGER libNewSize);
  16. STDMETHOD(CopyTo) (THIS_ IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten);
  17. STDMETHOD(Commit) (THIS_ DWORD grfCommitFlags);
  18. STDMETHOD(Revert) (THIS);
  19. STDMETHOD(LockRegion) (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
  20. STDMETHOD(UnlockRegion) (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
  21. STDMETHOD(Stat) (THIS_ STATSTG *pstatstg, DWORD grfStatFlag);
  22. STDMETHOD(Clone)(THIS_ IStream **ppstm);
  23. HRESULT Init(IStream *aStreams[], UINT cStreams, UINT uiCodePage);
  24. CStreamWrap();
  25. private:
  26. ~CStreamWrap();
  27. LONG _cRef;
  28. IStream *_aStreams[MAX_STREAMS];
  29. BOOL _fFirstReadForStream[MAX_STREAMS];
  30. UINT _cStreams;
  31. UINT _iCurStream;
  32. UINT _uiCodePage;
  33. UINT _uiBOM; // Byte order marker
  34. };
  35. CStreamWrap::CStreamWrap() : _cRef(1)
  36. {
  37. }
  38. CStreamWrap::~CStreamWrap()
  39. {
  40. while (_cStreams--)
  41. {
  42. if (_aStreams[_cStreams])
  43. {
  44. _aStreams[_cStreams]->Release();
  45. _aStreams[_cStreams] = NULL;
  46. }
  47. }
  48. }
  49. HRESULT CStreamWrap::Init(IStream *aStreams[], UINT cStreams, UINT uiCodePage)
  50. {
  51. if (cStreams > ARRAYSIZE(_aStreams))
  52. return E_FAIL;
  53. for (_cStreams = 0; _cStreams < cStreams; _cStreams++)
  54. {
  55. _aStreams[_cStreams] = aStreams[_cStreams];
  56. _fFirstReadForStream[_cStreams] = TRUE;
  57. _aStreams[_cStreams]->AddRef();
  58. }
  59. _uiCodePage = uiCodePage;
  60. _uiBOM = 0xfeff; // FEATURE - set default to byte order of machine
  61. return S_OK;
  62. }
  63. STDMETHODIMP CStreamWrap::QueryInterface(REFIID riid, void **ppv)
  64. {
  65. if (IsEqualIID(riid, IID_IStream) || IsEqualIID(riid, IID_IUnknown))
  66. {
  67. *ppv = SAFECAST(this, IStream *);
  68. }
  69. else
  70. {
  71. *ppv = NULL;
  72. return E_NOINTERFACE;
  73. }
  74. this->AddRef();
  75. return NOERROR;
  76. }
  77. STDMETHODIMP_(ULONG) CStreamWrap::AddRef()
  78. {
  79. return InterlockedIncrement(&this->_cRef);
  80. }
  81. STDMETHODIMP_(ULONG) CStreamWrap::Release()
  82. {
  83. if (InterlockedDecrement(&this->_cRef))
  84. return this->_cRef;
  85. delete this;
  86. return 0;
  87. }
  88. // Byte order marker macros
  89. #define IS_BOM_LITTLE_ENDIAN(pv) ((*(WORD*)pv) == 0xfffe)
  90. #define IS_BOM_BIG_ENDIAN(pv) ((*(WORD*)pv) == 0xfeff)
  91. STDMETHODIMP CStreamWrap::Read(void *pv, ULONG cb, ULONG *pcbRead)
  92. {
  93. ULONG cbReadTotal = 0;
  94. ULONG cbLeftToRead = cb;
  95. HRESULT hres = NOERROR;
  96. while (cbLeftToRead && (_iCurStream < _cStreams))
  97. {
  98. ULONG cbReadThisStream;
  99. hres = _aStreams[_iCurStream]->Read(pv, cbLeftToRead, &cbReadThisStream);
  100. // REVIEW: what if one stream's implementation returns a failure code
  101. // when reading at the end of the stream? We bail prematurely.
  102. if (SUCCEEDED(hres))
  103. {
  104. cbLeftToRead -= cbReadThisStream;
  105. if(_uiCodePage == CP_UNICODE)
  106. {
  107. if((_fFirstReadForStream[_iCurStream]) &&
  108. (cbReadThisStream >= 2) &&
  109. ((IS_BOM_LITTLE_ENDIAN(pv)) || (IS_BOM_BIG_ENDIAN(pv)))
  110. )
  111. {
  112. if(_iCurStream == 0)
  113. {
  114. _uiBOM = (*(WORD*)pv); // Save first streams byte order marker as default
  115. }
  116. else
  117. {
  118. // REVIEW: should handle swapping bytes to default for IE6
  119. if(_uiBOM != (*(WORD*)pv)) // BOM not default
  120. return(E_FAIL);
  121. // Skip past unicode document lead bytes
  122. cbReadThisStream -= 2;
  123. MoveMemory((BYTE*)pv, (BYTE*)pv+2, cbReadThisStream);
  124. }
  125. }
  126. _fFirstReadForStream[_iCurStream] = FALSE;
  127. }
  128. cbReadTotal += cbReadThisStream;
  129. pv = (char *)pv + cbReadThisStream;
  130. if (cbLeftToRead)
  131. {
  132. _iCurStream++;
  133. hres = S_OK;
  134. }
  135. }
  136. else
  137. break;
  138. }
  139. if (pcbRead)
  140. *pcbRead = cbReadTotal;
  141. if (SUCCEEDED(hres) && cbLeftToRead)
  142. hres = S_FALSE; // still success! but not completely
  143. return hres;
  144. }
  145. STDMETHODIMP CStreamWrap::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
  146. {
  147. if (pcbWritten)
  148. *pcbWritten = 0;
  149. return E_NOTIMPL;
  150. }
  151. // FEATURE: could at least support seaking to 0, as that's a common thing to do.
  152. // REVIEW: not too hard to implement thoroughly - cache Stat calls on each
  153. // substream (help implement ::Stat in this file too, which IMO is needed.)
  154. STDMETHODIMP CStreamWrap::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
  155. {
  156. return E_NOTIMPL;
  157. }
  158. STDMETHODIMP CStreamWrap::SetSize(ULARGE_INTEGER libNewSize)
  159. {
  160. return E_NOTIMPL;
  161. }
  162. //
  163. // REVIEW: this could use the internal buffer in the stream to avoid
  164. // extra buffer copies.
  165. //
  166. STDMETHODIMP CStreamWrap::CopyTo(IStream *pstmTo, ULARGE_INTEGER cb,
  167. ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
  168. {
  169. BYTE buf[512];
  170. ULONG cbRead;
  171. HRESULT hres = NOERROR;
  172. if (pcbRead)
  173. {
  174. pcbRead->LowPart = 0;
  175. pcbRead->HighPart = 0;
  176. }
  177. if (pcbWritten)
  178. {
  179. pcbWritten->LowPart = 0;
  180. pcbWritten->HighPart = 0;
  181. }
  182. ASSERT(cb.HighPart == 0);
  183. while (cb.LowPart)
  184. {
  185. hres = this->Read(buf, min(cb.LowPart, SIZEOF(buf)), &cbRead);
  186. if (FAILED(hres) || (cbRead == 0))
  187. break;
  188. if (pcbRead)
  189. pcbRead->LowPart += cbRead;
  190. cb.LowPart -= cbRead;
  191. hres = pstmTo->Write(buf, cbRead, &cbRead);
  192. if (pcbWritten)
  193. pcbWritten->LowPart += cbRead;
  194. if (FAILED(hres) || (cbRead == 0))
  195. break;
  196. }
  197. return hres;
  198. }
  199. STDMETHODIMP CStreamWrap::Commit(DWORD grfCommitFlags)
  200. {
  201. return NOERROR;
  202. }
  203. STDMETHODIMP CStreamWrap::Revert()
  204. {
  205. return E_NOTIMPL;
  206. }
  207. STDMETHODIMP CStreamWrap::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  208. {
  209. return E_NOTIMPL;
  210. }
  211. STDMETHODIMP CStreamWrap::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  212. {
  213. return E_NOTIMPL;
  214. }
  215. // FEATURE: you gotta support Stat, or Trident will barf on this stream.
  216. // Trivial to implement too, just call Stat on each sub-stream.
  217. STDMETHODIMP CStreamWrap::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
  218. {
  219. return E_NOTIMPL;
  220. }
  221. // REVIEW: so simple to implement, it's probably worth doing
  222. STDMETHODIMP CStreamWrap::Clone(IStream **ppstm)
  223. {
  224. return E_NOTIMPL;
  225. }
  226. // in:
  227. // ppstm array of stream pointers
  228. // cStreams number of streams in the array
  229. //
  230. SHDOCAPI SHCreateStreamWrapperCP(IStream *aStreams[], UINT cStreams, DWORD grfMode, UINT uiCodePage, IStream **ppstm)
  231. {
  232. HRESULT hres;
  233. *ppstm = NULL;
  234. if (grfMode != STGM_READ)
  235. return E_INVALIDARG;
  236. CStreamWrap *pwrap = new CStreamWrap();
  237. if (pwrap)
  238. {
  239. hres = pwrap->Init(aStreams, cStreams, uiCodePage);
  240. if (SUCCEEDED(hres))
  241. pwrap->QueryInterface(IID_IStream, (void **)ppstm);
  242. pwrap->Release();
  243. }
  244. else
  245. hres = E_OUTOFMEMORY;
  246. return hres;
  247. }