From 1f6d4f4384f36251560274f8566438ec8491df56 Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 8 May 2019 09:25:15 -0600 Subject: [PATCH] Added .NET Stream wrapper for COM IStream instances. --- Core/InteropServices/ComStream.cs | 157 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 Core/InteropServices/ComStream.cs diff --git a/Core/InteropServices/ComStream.cs b/Core/InteropServices/ComStream.cs new file mode 100644 index 00000000..30eb10ee --- /dev/null +++ b/Core/InteropServices/ComStream.cs @@ -0,0 +1,157 @@ +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); + } + } +} \ No newline at end of file