/************************************************************************************* Extended WPF Toolkit Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features, controls, and fast professional support, pick up the Plus Edition at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ /**************************************************************************\ Copyright Microsoft Corporation. All Rights Reserved. \**************************************************************************/ namespace Standard { using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; // disambiguate with System.Runtime.InteropServices.STATSTG using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG; // All these methods return void. Does the standard marshaller convert them to HRESULTs? /// /// Wraps a managed stream instance into an interface pointer consumable by COM. /// internal sealed class ManagedIStream : IStream, IDisposable { private const int STGTY_STREAM = 2; private const int STGM_READWRITE = 2; private const int LOCK_EXCLUSIVE = 2; private Stream _source; /// /// Initializes a new instance of the ManagedIStream class with the specified managed Stream object. /// /// /// The stream that this IStream reference is wrapping. /// public ManagedIStream(Stream source) { Verify.IsNotNull(source, "source"); _source = source; } private void _Validate() { if (null == _source) { throw new ObjectDisposedException("this"); } } // Comments are taken from MSDN IStream documentation. #region IStream Members /// /// Creates a new stream object with its own seek pointer that /// references the same bytes as the original stream. /// /// /// When this method returns, contains the new stream object. This parameter is passed uninitialized. /// /// /// For more information, see the existing documentation for IStream::Clone in the MSDN library. /// This class doesn't implement Clone. A COMException is thrown if it is used. /// [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Standard.HRESULT.ThrowIfFailed(System.String)")] [Obsolete("The method is not implemented", true)] public void Clone(out IStream ppstm) { ppstm = null; HRESULT.STG_E_INVALIDFUNCTION.ThrowIfFailed("The method is not implemented."); } /// /// Ensures that any changes made to a stream object that is open in transacted /// mode are reflected in the parent storage. /// /// /// A value that controls how the changes for the stream object are committed. /// /// /// For more information, see the existing documentation for IStream::Commit in the MSDN library. /// public void Commit(int grfCommitFlags) { _Validate(); _source.Flush(); } /// /// Copies a specified number of bytes from the current seek pointer in the /// stream to the current seek pointer in another stream. /// /// /// A reference to the destination stream. /// /// /// The number of bytes to copy from the source stream. /// /// /// On successful return, contains the actual number of bytes read from the source. /// (Note the native signature is to a ULARGE_INTEGER*, so 64 bits are written /// to this parameter on success.) /// /// /// On successful return, contains the actual number of bytes written to the destination. /// (Note the native signature is to a ULARGE_INTEGER*, so 64 bits are written /// to this parameter on success.) /// [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) { Verify.IsNotNull(pstm, "pstm"); _Validate(); // Reasonbly sized buffer, don't try to copy large streams in bulk. var buffer = new byte[4096]; long cbWritten = 0; while (cbWritten < cb) { int cbRead = _source.Read(buffer, 0, buffer.Length); if (0 == cbRead) { break; } // COM documentation is a bit vague here whether NULL is valid for the third parameter. // Going to assume it is, as most implementations I've seen treat it as optional. // It's possible this will break on some IStream implementations. pstm.Write(buffer, cbRead, IntPtr.Zero); cbWritten += cbRead; } if (IntPtr.Zero != pcbRead) { Marshal.WriteInt64(pcbRead, cbWritten); } if (IntPtr.Zero != pcbWritten) { Marshal.WriteInt64(pcbWritten, cbWritten); } } /// /// Restricts access to a specified range of bytes in the stream. /// /// /// The byte offset for the beginning of the range. /// /// /// The length of the range, in bytes, to restrict. /// /// /// The requested restrictions on accessing the range. /// /// /// For more information, see the existing documentation for IStream::LockRegion in the MSDN library. /// This class doesn't implement LockRegion. A COMException is thrown if it is used. /// [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Standard.HRESULT.ThrowIfFailed(System.String)"), Obsolete("The method is not implemented", true)] public void LockRegion(long libOffset, long cb, int dwLockType) { HRESULT.STG_E_INVALIDFUNCTION.ThrowIfFailed("The method is not implemented."); } /// /// Reads a specified number of bytes from the stream object into memory starting at the current seek pointer. /// /// /// When this method returns, contains the data read from the stream. This parameter is passed uninitialized. /// /// /// The number of bytes to read from the stream object. /// /// /// A pointer to a ULONG variable that receives the actual number of bytes read from the stream object. /// /// /// For more information, see the existing documentation for ISequentialStream::Read in the MSDN library. /// [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public void Read(byte[] pv, int cb, IntPtr pcbRead) { _Validate(); int cbRead = _source.Read(pv, 0, cb); if (IntPtr.Zero != pcbRead) { Marshal.WriteInt32(pcbRead, cbRead); } } /// /// Discards all changes that have been made to a transacted stream since the last Commit call. /// /// /// This class doesn't implement Revert. A COMException is thrown if it is used. /// [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Standard.HRESULT.ThrowIfFailed(System.String)"), Obsolete("The method is not implemented", true)] public void Revert() { HRESULT.STG_E_INVALIDFUNCTION.ThrowIfFailed("The method is not implemented."); } /// /// Changes the seek pointer to a new location relative to the beginning of the /// stream, to the end of the stream, or to the current seek pointer. /// /// /// The displacement to add to dwOrigin. /// /// /// The origin of the seek. The origin can be the beginning of the file, the current seek pointer, or the end of the file. /// /// /// On successful return, contains the offset of the seek pointer from the beginning of the stream. /// (Note the native signature is to a ULARGE_INTEGER*, so 64 bits are written /// to this parameter on success.) /// /// /// For more information, see the existing documentation for IStream::Seek in the MSDN library. /// [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) { _Validate(); long position = _source.Seek(dlibMove, (SeekOrigin)dwOrigin); if (IntPtr.Zero != plibNewPosition) { Marshal.WriteInt64(plibNewPosition, position); } } /// /// Changes the size of the stream object. /// /// /// The new size of the stream as a number of bytes. /// /// /// For more information, see the existing documentation for IStream::SetSize in the MSDN library. /// public void SetSize(long libNewSize) { _Validate(); _source.SetLength(libNewSize); } /// /// Retrieves the STATSTG structure for this stream. /// /// /// When this method returns, contains a STATSTG structure that describes this stream object. /// This parameter is passed uninitialized. /// /// /// Members in the STATSTG structure that this method does not return, thus saving some memory allocation operations. /// public void Stat(out STATSTG pstatstg, int grfStatFlag) { pstatstg = default(STATSTG); _Validate(); pstatstg.type = STGTY_STREAM; pstatstg.cbSize = _source.Length; pstatstg.grfMode = STGM_READWRITE; pstatstg.grfLocksSupported = LOCK_EXCLUSIVE; } /// /// Removes the access restriction on a range of bytes previously restricted with the LockRegion method. /// /// The byte offset for the beginning of the range. /// /// /// The length, in bytes, of the range to restrict. /// /// /// The access restrictions previously placed on the range. /// /// /// For more information, see the existing documentation for IStream::UnlockRegion in the MSDN library. /// This class doesn't implement UnlockRegion. A COMException is thrown if it is used. /// [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Standard.HRESULT.ThrowIfFailed(System.String)")] [Obsolete("The method is not implemented", true)] public void UnlockRegion(long libOffset, long cb, int dwLockType) { HRESULT.STG_E_INVALIDFUNCTION.ThrowIfFailed("The method is not implemented."); } /// /// Writes a specified number of bytes into the stream object starting at the current seek pointer. /// /// /// The buffer to write this stream to. /// /// /// The number of bytes to write to the stream. /// /// /// On successful return, contains the actual number of bytes written to the stream object. /// If the caller sets this pointer to null, this method does not provide the actual number /// of bytes written. /// [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public void Write(byte[] pv, int cb, IntPtr pcbWritten) { _Validate(); _source.Write(pv, 0, cb); if (IntPtr.Zero != pcbWritten) { Marshal.WriteInt32(pcbWritten, cb); } } #endregion #region IDisposable Members /// /// Releases resources controlled by this object. /// /// /// Dispose can be called multiple times, but trying to use the object /// after it has been disposed will generally throw ObjectDisposedExceptions. /// public void Dispose() { _source = null; } #endregion } }