using System; using System.IO; using System.Runtime.InteropServices.ComTypes; namespace Vanara.InteropServices { /// Implements a .NET stream over a COM IStream instance. /// public class ComStream : Stream { /// Initializes a new instance of the class. /// The supporting COM instance. /// stream public ComStream(IStream stream) => IStream = stream ?? throw new ArgumentNullException(nameof(stream)); /// Gets a value indicating whether the current stream supports reading. public override bool CanRead => true; /// Gets a value indicating whether the current stream supports seeking. public override bool CanSeek => true; /// Gets a value indicating whether the current stream supports writing. public override bool CanWrite => true; /// Gets the underlying stream. /// The underlying stream. public IStream IStream { get; private set; } /// Gets the length in bytes of the stream. /// iComStream public override long Length { get { if (IStream is null) throw new ObjectDisposedException(nameof(IStream)); IStream.Stat(out var statstg, 1 /* STATSFLAG_NONAME*/ ); return statstg.cbSize; } } /// Gets or sets the position within the current stream. public override long Position { get => Seek(0, SeekOrigin.Current); set => Seek(value, SeekOrigin.Begin); } /// /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. /// public override void Close() { if (IStream is null) return; IStream.Commit(0); IStream = null; } /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. public override void Flush() => IStream?.Commit(0); /// /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. /// /// /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between /// and ( + - 1) replaced by the bytes read from the /// current source. /// /// /// The zero-based byte offset in at which to begin storing the data read from the current stream. /// /// The maximum number of bytes to be read from the current stream. /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not /// currently available, or zero (0) if the end of the stream has been reached. /// /// iComStream /// Offsets other than zero are not supported. public override int Read(byte[] buffer, int offset, int count) { if (IStream is null) throw new ObjectDisposedException(nameof(IStream)); if (offset != 0) throw new NotSupportedException("Offsets other than zero are not supported."); var bytesRead = 0; using (var address = new PinnedObject(bytesRead)) IStream.Read(buffer, count, address); return bytesRead; } /// Sets the position within the current stream. /// A byte offset relative to the parameter. /// /// A value of type indicating the reference point used to obtain the new position. /// /// The new position within the current stream. /// iComStream public override long Seek(long offset, SeekOrigin origin) { if (IStream is null) throw new ObjectDisposedException(nameof(IStream)); var position = 0L; using (var address = new PinnedObject(position)) IStream.Seek(offset, (int)origin, address); return position; } /// Sets the length of the current stream. /// The desired length of the current stream in bytes. /// iComStream public override void SetLength(long value) { if (IStream is null) throw new ObjectDisposedException(nameof(IStream)); IStream.SetSize(value); } /// /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. /// /// /// An array of bytes. This method copies bytes from to the current stream. /// /// /// The zero-based byte offset in at which to begin copying bytes to the current stream. /// /// The number of bytes to be written to the current stream. /// iComStream /// Offsets other than zero are not supported. public override void Write(byte[] buffer, int offset, int count) { if (IStream is null) throw new ObjectDisposedException(nameof(IStream)); if (offset != 0) throw new NotSupportedException("Offsets other than zero are not supported."); IStream.Write(buffer, count, IntPtr.Zero); } /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (disposing) Close(); base.Dispose(disposing); } } }