File: Base\MS\Internal\IO\Packaging\CompoundFile\CFStream.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//
// <copyright file="CFStream.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//   Stream interface for manipulating data within a container stream.
//
// History:
//  05/13/2002: RogerCh: Initial implementation.
//  07/31/2002: RogerCh: Make obvious that we are using security suppressed interfaces.
//  05/20/2003: RogerCh: Ported to WCP tree.
//
//-----------------------------------------------------------------------------
 
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.IO.Packaging;
using MS.Internal.WindowsBase;
 
using System.Windows;
 
namespace MS.Internal.IO.Packaging.CompoundFile
{
/// <summary>
/// Class for manipulating data within container streams
/// </summary>
internal class CFStream : Stream
{
    //------------------------------------------------------
    //
    //  Public Properties
    //
    //------------------------------------------------------
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    public override bool CanRead
    {
        get
        {
            return (!StreamDisposed && (FileAccess.Read == (access & FileAccess.Read) ||
                    FileAccess.ReadWrite == (access & FileAccess.ReadWrite)));
        }
    }
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    public override bool CanSeek
    {
        get
        {
            // OLE32 DocFiles on local disk always seekable
            return (!StreamDisposed);       // unless it's disposed
        }
    }
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    public override bool CanWrite
    {
        get
        {
            return (!StreamDisposed && (FileAccess.Write     == (access & FileAccess.Write) ||
                    FileAccess.ReadWrite == (access & FileAccess.ReadWrite)));
        }
    }
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    public override long Length
    {
        get
        {
            CheckDisposedStatus();
            
            System.Runtime.InteropServices.ComTypes.STATSTG streamStat;
 
            // STATFLAG_NONAME required on IStream::Stat
            _safeIStream.Stat( out streamStat, SafeNativeCompoundFileConstants.STATFLAG_NONAME );
 
            return streamStat.cbSize;
        }
    }
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    public override long Position
    {
        get
        {
            CheckDisposedStatus();
            
            long seekPos = 0;
 
            _safeIStream.Seek(
                0, // Offset of zero
                SafeNativeCompoundFileConstants.STREAM_SEEK_CUR,
                out seekPos );
 
            return seekPos;
        }
        set
        {
            CheckDisposedStatus();
 
            if (!CanSeek)
            {
                throw new NotSupportedException(SR.Get(SRID.SetPositionNotSupported));
            }
            
            long seekPos = 0;
 
            _safeIStream.Seek(
                value , // given offset
                SafeNativeCompoundFileConstants.STREAM_SEEK_SET,
                out seekPos );
 
            if( value != seekPos )
            {
                throw new IOException(
                    SR.Get(SRID.SeekFailed));
            }
        }
    }
 
    //------------------------------------------------------
    //
    //  Public Methods
    //
    //------------------------------------------------------
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    public override void Flush()
    {
        CheckDisposedStatus();
        _safeIStream.Commit(0);
    }
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    /// <param name="offset">Offset byte count</param>
    /// <param name="origin">Offset origin</param>
    /// <returns></returns>
    public override long Seek( long offset, SeekOrigin origin )
    {
        CheckDisposedStatus();
 
        if (!CanSeek)
        {
            throw new NotSupportedException(SR.Get(SRID.SeekNotSupported));
        }
        
        // 
        long seekPos = 0;
        int translatedSeekOrigin = 0;
 
        switch(origin)
        {
            case SeekOrigin.Begin:
                translatedSeekOrigin = SafeNativeCompoundFileConstants.STREAM_SEEK_SET;
                if( 0 > offset )
                {
                    throw new ArgumentOutOfRangeException("offset",
                        SR.Get(SRID.SeekNegative));
                }
                break;
            case SeekOrigin.Current:
                translatedSeekOrigin = SafeNativeCompoundFileConstants.STREAM_SEEK_CUR;
                // Need to find the current seek pointer to see if we'll end
                //  up with a negative position.
                break;
            case SeekOrigin.End:
                translatedSeekOrigin = SafeNativeCompoundFileConstants.STREAM_SEEK_END;
                // Need to find the current seek pointer to see if we'll end
                //  up with a negative position.
                break;
            default:
                throw new System.ComponentModel.InvalidEnumArgumentException("origin", (int) origin, typeof(SeekOrigin));
        }
 
        _safeIStream.Seek( offset, translatedSeekOrigin, out seekPos );
 
        return seekPos;
    }
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    /// <param name="newLength">New length</param>
    public override void SetLength( long newLength )
    {
        CheckDisposedStatus();
 
        if (!CanWrite)
        {
            throw new NotSupportedException(SR.Get(SRID.SetLengthNotSupported));
        }
 
        if( 0 > newLength )
        {
            throw new ArgumentOutOfRangeException("newLength",
                SR.Get(SRID.StreamLengthNegative));
        }
        
        _safeIStream.SetSize( newLength );
 
        // updating the stream pointer if the stream has been truncated.
        if (newLength < this.Position)
            this.Position = newLength;
    }
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    /// <param name="buffer">Read data buffer</param>
    /// <param name="offset">Buffer start position</param>
    /// <param name="count">Number of bytes to read</param>
    /// <returns>Number of bytes actually read</returns>
    public override int Read( byte[] buffer, int offset, int count )
    {
        CheckDisposedStatus();
 
        PackagingUtilities.VerifyStreamReadArgs(this, buffer, offset, count);
 
        int read = 0;
 
        if ( 0 == offset ) // Zero offset is typical case
        {
            _safeIStream.Read( buffer, count, out read );
        }
        else // Non-zero offset
        {
            // Read into local array and then copy it into the given buffer at
            //  the specified offset.
            byte[] localBuffer = new byte[count];
            _safeIStream.Read( localBuffer, count, out read );
 
            if (read > 0)
                Array.Copy(localBuffer, 0, buffer, offset, read);
        }
 
        return read;
    }
 
    /// <summary>
    /// See .NET Framework SDK under System.IO.Stream
    /// </summary>
    /// <param name="buffer">Data buffer</param>
    /// <param name="offset">Buffer write start position</param>
    /// <param name="count">Number of bytes to write</param>
    public override void Write( byte[] buffer, int offset, int count )
    {
        CheckDisposedStatus();
 
        PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count);
 
        int written = 0; // CLR guys have deemed this uninteresting?
 
        if ( 0 == offset ) // Zero offset is typical case
        {
            _safeIStream.Write( buffer, count, out written );
        }
        else // Non-zero offset
        {
            // Copy from indicated offset to zero-based temp buffer
            byte[] localBuffer = new byte[count];
            Array.Copy(buffer, offset, localBuffer, 0, count);
            _safeIStream.Write( localBuffer, count, out written );
        }
 
        if( count != written )
            throw new IOException(
                SR.Get(SRID.WriteFailure));
 
    }
 
    //------------------------------------------------------
    //
    //  Internal Methods
    //
    //------------------------------------------------------
 
    // Check whether this Stream object is still valid.  If not, thrown an
    //  ObjectDisposedException.
    internal void CheckDisposedStatus()
    {
        if( StreamDisposed )
            throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed));
    }
 
    // Check whether this Stream object is still valid.
    internal bool StreamDisposed
    {
        get
        {
            return (backReference.StreamInfoDisposed || ( null == _safeIStream ));
        }
    }
 
    // Constructors
    internal CFStream(
        IStream underlyingStream,
        FileAccess openAccess,
        StreamInfo creator)
    {
        _safeIStream = underlyingStream;
        access = openAccess;
        backReference = creator;
    }
 
    //------------------------------------------------------
    //
    //  Protected Methods
    //
    //------------------------------------------------------
    /// <summary>
    /// Dispose(bool)
    /// </summary>
    /// <param name="disposing"></param>
    protected override void Dispose(bool disposing)
    {
        try
        {
            if (disposing)
            {
                // As per CLR docs for Stream.Close, calls to close on dead stream produces no exceptions.
                if (null != _safeIStream)
                {
                    // No need to call IStream.Commit, commiting at storage root level
                    //  is sufficient.
                    // Can't do Marshal.ReleaseComObject() here because there's only 
                    //  one COM ref for each RCW and there may be other users.
                    _safeIStream = null;
                }
            }
        }
        finally
        {
            base.Dispose(disposing);
        }
    }
 
    //------------------------------------------------------
    //
    //  Private Members
    //
    //------------------------------------------------------
    IStream _safeIStream;
    FileAccess access;
 
    /// <summary>
    /// If only this stream object is held open, and the rest of the container
    /// has not been explicitly closed, we need this to keep the rest of the
    /// tree open because the CLR GC doesn't realize that our IStream has
    /// a dependency on the rest of the container object tree.
    /// </summary>
    StreamInfo backReference;
 
}
}