298 lines
10 KiB
C#
298 lines
10 KiB
C#
|
|
//
|
|
// 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 DTS.Common.Utilities.IO.MemoryMap
|
|
{
|
|
|
|
/// <summary>
|
|
/// Specifies page protection for the mapped file
|
|
/// These correspond to the PAGE_XXX set of flags
|
|
/// passed to CreateFileMapping()
|
|
/// </summary>
|
|
[Flags]
|
|
public enum MapProtection
|
|
{
|
|
#pragma warning disable 1591 //disable xml comment warnings - these are from the Win32 API (I think)
|
|
PageNone = 0x00000000,
|
|
// protection
|
|
PageReadOnly = 0x00000002,
|
|
PageReadWrite = 0x00000004,
|
|
PageWriteCopy = 0x00000008,
|
|
// attributes
|
|
SecImage = 0x01000000,
|
|
SecReserve = 0x04000000,
|
|
SecCommit = 0x08000000,
|
|
SecNoCache = 0x10000000,
|
|
#pragma warning restore 1591
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Allows you to read/write from/to
|
|
/// a view of a memory mapped file.
|
|
/// </summary>
|
|
public class MapViewStream : Stream, IDisposable
|
|
{
|
|
|
|
#region variables
|
|
|
|
//! What's our access?
|
|
private readonly MapProtection _protection = MapProtection.PageNone;
|
|
|
|
//! base address of our buffer
|
|
private IntPtr _base = IntPtr.Zero;
|
|
|
|
//! our current buffer length
|
|
private readonly long _length = 0;
|
|
|
|
//! our current position in the stream buffer
|
|
private long _position = 0;
|
|
|
|
#endregion // variables
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Constructor used internally by MemoryMappedFile.
|
|
/// </summary>
|
|
/// <param name="baseAddress">base address where the view starts</param>
|
|
/// <param name="length">Length of view, in bytes</param>
|
|
/// <param name="protection"></param>
|
|
internal MapViewStream(IntPtr baseAddress, long length,
|
|
MapProtection protection)
|
|
{
|
|
_base = baseAddress;
|
|
_length = length;
|
|
_protection = protection;
|
|
_position = 0;
|
|
IsOpen = (baseAddress != IntPtr.Zero);
|
|
}
|
|
/// <summary>
|
|
/// finalizer for MapViewStream.
|
|
/// </summary>
|
|
~MapViewStream()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
|
|
#region Properties
|
|
|
|
public override bool CanRead => true;
|
|
|
|
public override bool CanSeek => true;
|
|
|
|
public override bool CanWrite => (((int)_protection) & 0x000000C) != 0;
|
|
public override long Length => _length;
|
|
|
|
public override long Position
|
|
{
|
|
get => _position;
|
|
set => Seek(value, SeekOrigin.Begin);
|
|
}
|
|
private bool IsOpen { get; set; } = false;
|
|
|
|
#endregion // Properties
|
|
|
|
|
|
#region Stream Methods
|
|
|
|
/// <summary>
|
|
/// causes any buffered data to be written to the stream and clears the buffer
|
|
/// </summary>
|
|
public override void Flush()
|
|
{
|
|
try
|
|
{
|
|
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);
|
|
}
|
|
catch (Exception) { }
|
|
}
|
|
/// <summary>
|
|
/// reads a sequence of bytes from the current stream and advances the position in the stream by the number
|
|
/// of bytes read
|
|
/// </summary>
|
|
/// <param name="buffer">array of bytes, will contain the bytes read from the stream offset by
|
|
/// "offset"</param>
|
|
/// <param name="offset">the offset from the start of buffer to store read data</param>
|
|
/// <param name="count">the maximum number of bytes to read</param>
|
|
/// <returns>the number of bytes read into the buffer
|
|
/// Arguement Exception if count is greater than buffer after offset is removed
|
|
/// ObjectDisposedException if stream is not open
|
|
/// </returns>
|
|
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");
|
|
|
|
var bytesToRead = (int)System.Math.Min(Length - _position, count);
|
|
Marshal.Copy((IntPtr)(_base.ToInt64() + _position), buffer, offset, bytesToRead);
|
|
|
|
_position += bytesToRead;
|
|
return bytesToRead;
|
|
}
|
|
//Added by Tadd 2009.07.01:
|
|
public int ReadShort(short[] buffer, int offset, int count)
|
|
{
|
|
if (!IsOpen)
|
|
throw new ObjectDisposedException("Stream is closed");
|
|
|
|
if (buffer.Length - offset < count)
|
|
throw new ArgumentException("Invalid Offset");
|
|
|
|
// If the above exceptions are thrown and handled the method goes on to return
|
|
// as many of the requested objects as it can.
|
|
var shortsToRead = (int)System.Math.Min(Length - _position, count);
|
|
Marshal.Copy((IntPtr)(_base.ToInt64() + _position), buffer, offset, shortsToRead);
|
|
|
|
_position += shortsToRead;
|
|
return shortsToRead;
|
|
}
|
|
/// <summary>
|
|
/// writes specified number of bytes to the stream starting at an offset
|
|
/// </summary>
|
|
/// <param name="buffer">array containing the bytes to be written</param>
|
|
/// <param name="offset">the offset from the start of buffer to start reading from</param>
|
|
/// <param name="count">maximum number of bytes to write</param>
|
|
/// <remarks>
|
|
/// throws ObjectDisposedException if the stream is no longer open
|
|
/// throws FileMapIOException if the stream cannot be written to
|
|
/// throws ArgumentException if count is greater than buffer length after removing offset
|
|
/// </remarks>
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
if (!IsOpen)
|
|
throw new ObjectDisposedException("Stream is closed");
|
|
if (!CanWrite)
|
|
throw new FileMapIOException("Stream cannot be written to");
|
|
|
|
if (buffer.Length - offset < count)
|
|
throw new ArgumentException("Invalid Offset");
|
|
|
|
var bytesToWrite = (int)System.Math.Min(Length - _position, count);
|
|
if (bytesToWrite == 0)
|
|
return;
|
|
|
|
Marshal.Copy(buffer, offset, (IntPtr)(_base.ToInt64() + _position), bytesToWrite);
|
|
|
|
_position += bytesToWrite;
|
|
}
|
|
|
|
/// <summary>
|
|
/// move to a specified position in the stream
|
|
/// </summary>
|
|
/// <param name="offset">a byte offset relative to the origin parameter</param>
|
|
/// <param name="origin"><see cref="System.IO.SeekOrigin" /> position to seek relative to
|
|
/// (current, beginning, end)
|
|
/// </param>
|
|
/// <returns>new position in the stream</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// not supported
|
|
/// sets the length of the current stream
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
public override void SetLength(long value)
|
|
{
|
|
// not supported!
|
|
throw new NotSupportedException("Can't change View Size");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close the current stream and release any resources
|
|
/// </summary>
|
|
/// <remarks>generally close isn't overriden?</remarks>
|
|
public override void Close()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
#endregion // Stream methods
|
|
|
|
|
|
#region IDisposable Implementation
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// release unmanaged resources
|
|
/// </summary>
|
|
/// <param name="disposing"></param>
|
|
/// <remarks>virtual void Dispose functions can be dangerous
|
|
/// as overriding it in an inherited class might mean you miss
|
|
/// the base class Dispose
|
|
/// </remarks>
|
|
protected override 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
|