Leaked source code of windows server 2003
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.

304 lines
8.2 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. ASSERT( 0 != this->_cRef );
  84. ULONG cRef = InterlockedDecrement(&this->_cRef);
  85. if ( 0 == cRef )
  86. {
  87. delete this;
  88. }
  89. return cRef;
  90. }
  91. // Byte order marker macros
  92. #define IS_BOM_LITTLE_ENDIAN(pv) ((*(WORD*)pv) == 0xfffe)
  93. #define IS_BOM_BIG_ENDIAN(pv) ((*(WORD*)pv) == 0xfeff)
  94. STDMETHODIMP CStreamWrap::Read(void *pv, ULONG cb, ULONG *pcbRead)
  95. {
  96. ULONG cbReadTotal = 0;
  97. ULONG cbLeftToRead = cb;
  98. HRESULT hres = NOERROR;
  99. while (cbLeftToRead && (_iCurStream < _cStreams))
  100. {
  101. ULONG cbReadThisStream;
  102. hres = _aStreams[_iCurStream]->Read(pv, cbLeftToRead, &cbReadThisStream);
  103. // REVIEW: what if one stream's implementation returns a failure code
  104. // when reading at the end of the stream? We bail prematurely.
  105. if (SUCCEEDED(hres))
  106. {
  107. cbLeftToRead -= cbReadThisStream;
  108. if(_uiCodePage == CP_UNICODE)
  109. {
  110. if((_fFirstReadForStream[_iCurStream]) &&
  111. (cbReadThisStream >= 2) &&
  112. ((IS_BOM_LITTLE_ENDIAN(pv)) || (IS_BOM_BIG_ENDIAN(pv)))
  113. )
  114. {
  115. if(_iCurStream == 0)
  116. {
  117. _uiBOM = (*(WORD*)pv); // Save first streams byte order marker as default
  118. }
  119. else
  120. {
  121. // REVIEW: should handle swapping bytes to default for IE6
  122. if(_uiBOM != (*(WORD*)pv)) // BOM not default
  123. return(E_FAIL);
  124. // Skip past unicode document lead bytes
  125. cbReadThisStream -= 2;
  126. MoveMemory((BYTE*)pv, (BYTE*)pv+2, cbReadThisStream);
  127. }
  128. }
  129. _fFirstReadForStream[_iCurStream] = FALSE;
  130. }
  131. cbReadTotal += cbReadThisStream;
  132. pv = (char *)pv + cbReadThisStream;
  133. if (cbLeftToRead)
  134. {
  135. _iCurStream++;
  136. hres = S_OK;
  137. }
  138. }
  139. else
  140. break;
  141. }
  142. if (pcbRead)
  143. *pcbRead = cbReadTotal;
  144. if (SUCCEEDED(hres) && cbLeftToRead)
  145. hres = S_FALSE; // still success! but not completely
  146. return hres;
  147. }
  148. STDMETHODIMP CStreamWrap::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
  149. {
  150. if (pcbWritten)
  151. *pcbWritten = 0;
  152. return E_NOTIMPL;
  153. }
  154. // FEATURE: could at least support seaking to 0, as that's a common thing to do.
  155. // REVIEW: not too hard to implement thoroughly - cache Stat calls on each
  156. // substream (help implement ::Stat in this file too, which IMO is needed.)
  157. STDMETHODIMP CStreamWrap::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
  158. {
  159. return E_NOTIMPL;
  160. }
  161. STDMETHODIMP CStreamWrap::SetSize(ULARGE_INTEGER libNewSize)
  162. {
  163. return E_NOTIMPL;
  164. }
  165. //
  166. // REVIEW: this could use the internal buffer in the stream to avoid
  167. // extra buffer copies.
  168. //
  169. STDMETHODIMP CStreamWrap::CopyTo(IStream *pstmTo, ULARGE_INTEGER cb,
  170. ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
  171. {
  172. BYTE buf[512];
  173. ULONG cbRead;
  174. HRESULT hres = NOERROR;
  175. if (pcbRead)
  176. {
  177. pcbRead->LowPart = 0;
  178. pcbRead->HighPart = 0;
  179. }
  180. if (pcbWritten)
  181. {
  182. pcbWritten->LowPart = 0;
  183. pcbWritten->HighPart = 0;
  184. }
  185. ASSERT(cb.HighPart == 0);
  186. while (cb.LowPart)
  187. {
  188. hres = this->Read(buf, min(cb.LowPart, SIZEOF(buf)), &cbRead);
  189. if (FAILED(hres) || (cbRead == 0))
  190. break;
  191. if (pcbRead)
  192. pcbRead->LowPart += cbRead;
  193. cb.LowPart -= cbRead;
  194. hres = pstmTo->Write(buf, cbRead, &cbRead);
  195. if (pcbWritten)
  196. pcbWritten->LowPart += cbRead;
  197. if (FAILED(hres) || (cbRead == 0))
  198. break;
  199. }
  200. return hres;
  201. }
  202. STDMETHODIMP CStreamWrap::Commit(DWORD grfCommitFlags)
  203. {
  204. return NOERROR;
  205. }
  206. STDMETHODIMP CStreamWrap::Revert()
  207. {
  208. return E_NOTIMPL;
  209. }
  210. STDMETHODIMP CStreamWrap::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  211. {
  212. return E_NOTIMPL;
  213. }
  214. STDMETHODIMP CStreamWrap::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  215. {
  216. return E_NOTIMPL;
  217. }
  218. // FEATURE: you gotta support Stat, or Trident will barf on this stream.
  219. // Trivial to implement too, just call Stat on each sub-stream.
  220. STDMETHODIMP CStreamWrap::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
  221. {
  222. return E_NOTIMPL;
  223. }
  224. // REVIEW: so simple to implement, it's probably worth doing
  225. STDMETHODIMP CStreamWrap::Clone(IStream **ppstm)
  226. {
  227. return E_NOTIMPL;
  228. }
  229. // in:
  230. // ppstm array of stream pointers
  231. // cStreams number of streams in the array
  232. //
  233. SHDOCAPI SHCreateStreamWrapperCP(IStream *aStreams[], UINT cStreams, DWORD grfMode, UINT uiCodePage, IStream **ppstm)
  234. {
  235. HRESULT hres;
  236. *ppstm = NULL;
  237. if (grfMode != STGM_READ)
  238. return E_INVALIDARG;
  239. CStreamWrap *pwrap = new CStreamWrap();
  240. if (pwrap)
  241. {
  242. hres = pwrap->Init(aStreams, cStreams, uiCodePage);
  243. if (SUCCEEDED(hres))
  244. pwrap->QueryInterface(IID_IStream, (void **)ppstm);
  245. pwrap->Release();
  246. }
  247. else
  248. hres = E_OUTOFMEMORY;
  249. return hres;
  250. }