//
// MapViewStream.cs
//
// Implementation of a library to use Win32 Memory Mapped
// Files from within .NET applications
//
// COPYRIGHT (C) 2001, Tomas Restrepo (tomasr@mvps.org)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Winterdom.IO.FileMap
{
///
/// Specifies page protection for the mapped file
/// These correspond to the PAGE_XXX set of flags
/// passed to CreateFileMapping()
///
[ Flags ]
public enum MapProtection
{
PageNone = 0x00000000,
// protection
PageReadOnly = 0x00000002,
PageReadWrite = 0x00000004,
PageWriteCopy = 0x00000008,
// attributes
SecImage = 0x01000000,
SecReserve = 0x04000000,
SecCommit = 0x08000000,
SecNoCache = 0x10000000,
}
///
/// Allows you to read/write from/to
/// a view of a memory mapped file.
///
public class MapViewStream : Stream, IDisposable
{
#region variables
//! What's our access?
MapProtection _protection = MapProtection.PageNone;
//! base address of our buffer
IntPtr _base = IntPtr.Zero;
//! our current buffer length
long _length = 0;
//! our current position in the stream buffer
long _position = 0;
//! are we open?
bool _isOpen = false;
#endregion // variables
///
/// Constructor used internally by MemoryMappedFile.
///
/// base address where the view starts
/// Length of view, in bytes
///
internal MapViewStream ( IntPtr baseAddress, long length,
MapProtection protection )
{
_base = baseAddress;
_length = length;
_protection = protection;
_position = 0;
_isOpen = (baseAddress != IntPtr.Zero);
}
~MapViewStream()
{
Dispose(false);
}
#region Properties
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek {
get { return true; }
}
public override bool CanWrite {
get { return (((int)_protection) & 0x000000C) != 0; }
}
public override long Length {
get { return _length; }
}
public override long Position {
get { return _position; }
set { Seek(value, SeekOrigin.Begin); }
}
private bool IsOpen {
get { return _isOpen; }
set { _isOpen = value; }
}
#endregion // Properties
#region Stream Methods
public override void Flush()
{
if ( !IsOpen )
throw new ObjectDisposedException ( "Stream is closed" );
// flush the view but leave the buffer intact
// FIX: get rid of cast
Win32MapApis.FlushViewOfFile ( _base, (int)_length );
}
public override int Read ( byte[] buffer, int offset, int count )
{
if ( !IsOpen )
throw new ObjectDisposedException ( "Stream is closed" );
if ( buffer.Length - offset < count )
throw new ArgumentException ( "Invalid Offset" );
int bytesToRead = (int)Math.Min(Length-_position, count);
Marshal.Copy((IntPtr)(_base.ToInt64()+_position), buffer, offset, bytesToRead);
_position += bytesToRead;
return bytesToRead;
}
public override void Write ( byte[] buffer, int offset, int count )
{
if ( !this.IsOpen )
throw new ObjectDisposedException("Stream is closed");
if ( !this.CanWrite )
throw new FileMapIOException ( "Stream cannot be written to" );
if ( buffer.Length - offset < count )
throw new ArgumentException ( "Invalid Offset" );
int bytesToWrite = (int)Math.Min(Length-_position, count);
if ( bytesToWrite==0 )
return;
Marshal.Copy(buffer, offset, (IntPtr)(_base.ToInt64()+_position), bytesToWrite);
_position += bytesToWrite;
}
public override long Seek ( long offset, SeekOrigin origin )
{
if ( !IsOpen )
throw new ObjectDisposedException ( "Stream is closed" );
long newpos = 0;
switch ( origin )
{
case SeekOrigin.Begin: newpos = offset; break;
case SeekOrigin.Current: newpos = Position + offset; break;
case SeekOrigin.End: newpos = Length + offset; break;
}
// sanity check
if ( newpos < 0 || newpos > Length )
throw new FileMapIOException ( "Invalid Seek Offset" );
_position = newpos;
return newpos;
}
public override void SetLength ( long value )
{
// not supported!
throw new NotSupportedException ( "Can't change View Size" );
}
public override void Close()
{
Dispose(true);
}
#endregion // Stream methods
#region IDisposable Implementation
public new void Dispose()
{
Dispose(true);
}
protected virtual new void Dispose(bool disposing)
{
if ( IsOpen )
{
Flush();
// FIX: eliminate cast
Win32MapApis.UnmapViewOfFile ( _base );
IsOpen = false;
}
if ( disposing )
GC.SuppressFinalize(this);
}
#endregion // IDisposable Implementation
} // class MapViewStream
} // namespace Winterdom.IO.FileMap