StreamHelper.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*************************************************************************************
  2. Extended WPF Toolkit
  3. Copyright (C) 2007-2013 Xceed Software Inc.
  4. This program is provided to you under the terms of the Microsoft Public
  5. License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
  6. For more features, controls, and fast professional support,
  7. pick up the Plus Edition at http://xceed.com/wpf_toolkit
  8. Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids
  9. ***********************************************************************************/
  10. /**************************************************************************\
  11. Copyright Microsoft Corporation. All Rights Reserved.
  12. \**************************************************************************/
  13. namespace Standard
  14. {
  15. using System;
  16. using System.Diagnostics.CodeAnalysis;
  17. using System.IO;
  18. using System.Runtime.InteropServices;
  19. using System.Runtime.InteropServices.ComTypes;
  20. // disambiguate with System.Runtime.InteropServices.STATSTG
  21. using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
  22. // All these methods return void. Does the standard marshaller convert them to HRESULTs?
  23. /// <summary>
  24. /// Wraps a managed stream instance into an interface pointer consumable by COM.
  25. /// </summary>
  26. internal sealed class ManagedIStream : IStream, IDisposable
  27. {
  28. private const int STGTY_STREAM = 2;
  29. private const int STGM_READWRITE = 2;
  30. private const int LOCK_EXCLUSIVE = 2;
  31. private Stream _source;
  32. /// <summary>
  33. /// Initializes a new instance of the ManagedIStream class with the specified managed Stream object.
  34. /// </summary>
  35. /// <param name="source">
  36. /// The stream that this IStream reference is wrapping.
  37. /// </param>
  38. public ManagedIStream(Stream source)
  39. {
  40. Verify.IsNotNull(source, "source");
  41. _source = source;
  42. }
  43. private void _Validate()
  44. {
  45. if (null == _source)
  46. {
  47. throw new ObjectDisposedException("this");
  48. }
  49. }
  50. // Comments are taken from MSDN IStream documentation.
  51. #region IStream Members
  52. /// <summary>
  53. /// Creates a new stream object with its own seek pointer that
  54. /// references the same bytes as the original stream.
  55. /// </summary>
  56. /// <param name="ppstm">
  57. /// When this method returns, contains the new stream object. This parameter is passed uninitialized.
  58. /// </param>
  59. /// <remarks>
  60. /// For more information, see the existing documentation for IStream::Clone in the MSDN library.
  61. /// This class doesn't implement Clone. A COMException is thrown if it is used.
  62. /// </remarks>
  63. [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Standard.HRESULT.ThrowIfFailed(System.String)")]
  64. [Obsolete("The method is not implemented", true)]
  65. public void Clone(out IStream ppstm)
  66. {
  67. ppstm = null;
  68. HRESULT.STG_E_INVALIDFUNCTION.ThrowIfFailed("The method is not implemented.");
  69. }
  70. /// <summary>
  71. /// Ensures that any changes made to a stream object that is open in transacted
  72. /// mode are reflected in the parent storage.
  73. /// </summary>
  74. /// <param name="grfCommitFlags">
  75. /// A value that controls how the changes for the stream object are committed.
  76. /// </param>
  77. /// <remarks>
  78. /// For more information, see the existing documentation for IStream::Commit in the MSDN library.
  79. /// </remarks>
  80. public void Commit(int grfCommitFlags)
  81. {
  82. _Validate();
  83. _source.Flush();
  84. }
  85. /// <summary>
  86. /// Copies a specified number of bytes from the current seek pointer in the
  87. /// stream to the current seek pointer in another stream.
  88. /// </summary>
  89. /// <param name="pstm">
  90. /// A reference to the destination stream.
  91. /// </param>
  92. /// <param name="cb">
  93. /// The number of bytes to copy from the source stream.
  94. /// </param>
  95. /// <param name="pcbRead">
  96. /// On successful return, contains the actual number of bytes read from the source.
  97. /// (Note the native signature is to a ULARGE_INTEGER*, so 64 bits are written
  98. /// to this parameter on success.)
  99. /// </param>
  100. /// <param name="pcbWritten">
  101. /// On successful return, contains the actual number of bytes written to the destination.
  102. /// (Note the native signature is to a ULARGE_INTEGER*, so 64 bits are written
  103. /// to this parameter on success.)
  104. /// </param>
  105. [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
  106. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  107. public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
  108. {
  109. Verify.IsNotNull(pstm, "pstm");
  110. _Validate();
  111. // Reasonbly sized buffer, don't try to copy large streams in bulk.
  112. var buffer = new byte[4096];
  113. long cbWritten = 0;
  114. while (cbWritten < cb)
  115. {
  116. int cbRead = _source.Read(buffer, 0, buffer.Length);
  117. if (0 == cbRead)
  118. {
  119. break;
  120. }
  121. // COM documentation is a bit vague here whether NULL is valid for the third parameter.
  122. // Going to assume it is, as most implementations I've seen treat it as optional.
  123. // It's possible this will break on some IStream implementations.
  124. pstm.Write(buffer, cbRead, IntPtr.Zero);
  125. cbWritten += cbRead;
  126. }
  127. if (IntPtr.Zero != pcbRead)
  128. {
  129. Marshal.WriteInt64(pcbRead, cbWritten);
  130. }
  131. if (IntPtr.Zero != pcbWritten)
  132. {
  133. Marshal.WriteInt64(pcbWritten, cbWritten);
  134. }
  135. }
  136. /// <summary>
  137. /// Restricts access to a specified range of bytes in the stream.
  138. /// </summary>
  139. /// <param name="libOffset">
  140. /// The byte offset for the beginning of the range.
  141. /// </param>
  142. /// <param name="cb">
  143. /// The length of the range, in bytes, to restrict.
  144. /// </param>
  145. /// <param name="dwLockType">
  146. /// The requested restrictions on accessing the range.
  147. /// </param>
  148. /// <remarks>
  149. /// For more information, see the existing documentation for IStream::LockRegion in the MSDN library.
  150. /// This class doesn't implement LockRegion. A COMException is thrown if it is used.
  151. /// </remarks>
  152. [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Standard.HRESULT.ThrowIfFailed(System.String)"), Obsolete("The method is not implemented", true)]
  153. public void LockRegion(long libOffset, long cb, int dwLockType)
  154. {
  155. HRESULT.STG_E_INVALIDFUNCTION.ThrowIfFailed("The method is not implemented.");
  156. }
  157. /// <summary>
  158. /// Reads a specified number of bytes from the stream object into memory starting at the current seek pointer.
  159. /// </summary>
  160. /// <param name="pv">
  161. /// When this method returns, contains the data read from the stream. This parameter is passed uninitialized.
  162. /// </param>
  163. /// <param name="cb">
  164. /// The number of bytes to read from the stream object.
  165. /// </param>
  166. /// <param name="pcbRead">
  167. /// A pointer to a ULONG variable that receives the actual number of bytes read from the stream object.
  168. /// </param>
  169. /// <remarks>
  170. /// For more information, see the existing documentation for ISequentialStream::Read in the MSDN library.
  171. /// </remarks>
  172. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  173. public void Read(byte[] pv, int cb, IntPtr pcbRead)
  174. {
  175. _Validate();
  176. int cbRead = _source.Read(pv, 0, cb);
  177. if (IntPtr.Zero != pcbRead)
  178. {
  179. Marshal.WriteInt32(pcbRead, cbRead);
  180. }
  181. }
  182. /// <summary>
  183. /// Discards all changes that have been made to a transacted stream since the last Commit call.
  184. /// </summary>
  185. /// <remarks>
  186. /// This class doesn't implement Revert. A COMException is thrown if it is used.
  187. /// </remarks>
  188. [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Standard.HRESULT.ThrowIfFailed(System.String)"), Obsolete("The method is not implemented", true)]
  189. public void Revert()
  190. {
  191. HRESULT.STG_E_INVALIDFUNCTION.ThrowIfFailed("The method is not implemented.");
  192. }
  193. /// <summary>
  194. /// Changes the seek pointer to a new location relative to the beginning of the
  195. /// stream, to the end of the stream, or to the current seek pointer.
  196. /// </summary>
  197. /// <param name="dlibMove">
  198. /// The displacement to add to dwOrigin.
  199. /// </param>
  200. /// <param name="dwOrigin">
  201. /// The origin of the seek. The origin can be the beginning of the file, the current seek pointer, or the end of the file.
  202. /// </param>
  203. /// <param name="plibNewPosition">
  204. /// On successful return, contains the offset of the seek pointer from the beginning of the stream.
  205. /// (Note the native signature is to a ULARGE_INTEGER*, so 64 bits are written
  206. /// to this parameter on success.)
  207. /// </param>
  208. /// <remarks>
  209. /// For more information, see the existing documentation for IStream::Seek in the MSDN library.
  210. /// </remarks>
  211. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  212. public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
  213. {
  214. _Validate();
  215. long position = _source.Seek(dlibMove, (SeekOrigin)dwOrigin);
  216. if (IntPtr.Zero != plibNewPosition)
  217. {
  218. Marshal.WriteInt64(plibNewPosition, position);
  219. }
  220. }
  221. /// <summary>
  222. /// Changes the size of the stream object.
  223. /// </summary>
  224. /// <param name="libNewSize">
  225. /// The new size of the stream as a number of bytes.
  226. /// </param>
  227. /// <remarks>
  228. /// For more information, see the existing documentation for IStream::SetSize in the MSDN library.
  229. /// </remarks>
  230. public void SetSize(long libNewSize)
  231. {
  232. _Validate();
  233. _source.SetLength(libNewSize);
  234. }
  235. /// <summary>
  236. /// Retrieves the STATSTG structure for this stream.
  237. /// </summary>
  238. /// <param name="pstatstg">
  239. /// When this method returns, contains a STATSTG structure that describes this stream object.
  240. /// This parameter is passed uninitialized.
  241. /// </param>
  242. /// <param name="grfStatFlag">
  243. /// Members in the STATSTG structure that this method does not return, thus saving some memory allocation operations.
  244. /// </param>
  245. public void Stat(out STATSTG pstatstg, int grfStatFlag)
  246. {
  247. pstatstg = default(STATSTG);
  248. _Validate();
  249. pstatstg.type = STGTY_STREAM;
  250. pstatstg.cbSize = _source.Length;
  251. pstatstg.grfMode = STGM_READWRITE;
  252. pstatstg.grfLocksSupported = LOCK_EXCLUSIVE;
  253. }
  254. /// <summary>
  255. /// Removes the access restriction on a range of bytes previously restricted with the LockRegion method.
  256. /// </summary>
  257. /// <param name="libOffset">The byte offset for the beginning of the range.
  258. /// </param>
  259. /// <param name="cb">
  260. /// The length, in bytes, of the range to restrict.
  261. /// </param>
  262. /// <param name="dwLockType">
  263. /// The access restrictions previously placed on the range.
  264. /// </param>
  265. /// <remarks>
  266. /// For more information, see the existing documentation for IStream::UnlockRegion in the MSDN library.
  267. /// This class doesn't implement UnlockRegion. A COMException is thrown if it is used.
  268. /// </remarks>
  269. [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Standard.HRESULT.ThrowIfFailed(System.String)")]
  270. [Obsolete("The method is not implemented", true)]
  271. public void UnlockRegion(long libOffset, long cb, int dwLockType)
  272. {
  273. HRESULT.STG_E_INVALIDFUNCTION.ThrowIfFailed("The method is not implemented.");
  274. }
  275. /// <summary>
  276. /// Writes a specified number of bytes into the stream object starting at the current seek pointer.
  277. /// </summary>
  278. /// <param name="pv">
  279. /// The buffer to write this stream to.
  280. /// </param>
  281. /// <param name="cb">
  282. /// The number of bytes to write to the stream.
  283. /// </param>
  284. /// <param name="pcbWritten">
  285. /// On successful return, contains the actual number of bytes written to the stream object.
  286. /// If the caller sets this pointer to null, this method does not provide the actual number
  287. /// of bytes written.
  288. /// </param>
  289. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  290. public void Write(byte[] pv, int cb, IntPtr pcbWritten)
  291. {
  292. _Validate();
  293. _source.Write(pv, 0, cb);
  294. if (IntPtr.Zero != pcbWritten)
  295. {
  296. Marshal.WriteInt32(pcbWritten, cb);
  297. }
  298. }
  299. #endregion
  300. #region IDisposable Members
  301. /// <summary>
  302. /// Releases resources controlled by this object.
  303. /// </summary>
  304. /// <remarks>
  305. /// Dispose can be called multiple times, but trying to use the object
  306. /// after it has been disposed will generally throw ObjectDisposedExceptions.
  307. /// </remarks>
  308. public void Dispose()
  309. {
  310. _source = null;
  311. }
  312. #endregion
  313. }
  314. }