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);
}
}
}