//
// MemoryMappedFile.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 access for the mapped file.
/// These correspond to the FILE_MAP_XXX
/// constants used by MapViewOfFile[Ex]()
///
public enum MapAccess
{
FileMapCopy = 0x0001,
FileMapWrite = 0x0002,
FileMapRead = 0x0004,
FileMapAllAccess = 0x001f,
}
/// Wrapper class around the Win32 MMF APIs
///
/// Allows you to easily use memory mapped files on
/// .NET applications.
/// Currently, not all functionality provided by
/// the Win32 system is avaliable. Things that are not
/// supported include:
///
/// - You can't specify security descriptors
/// - You can't build the memory mapped file
/// on top of a System.IO.File already opened
///
/// The class is currently MarshalByRefObject, but I would
/// be careful about possible interactions!
///
public class MemoryMappedFile : MarshalByRefObject, IDisposable
{
//! handle to MemoryMappedFile object
private IntPtr _hMap = IntPtr.Zero;
#region Constants
private const int GENERIC_READ = unchecked((int)0x80000000);
private const int GENERIC_WRITE = unchecked((int)0x40000000);
private const int OPEN_ALWAYS = 4;
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private static readonly IntPtr NULL_HANDLE = IntPtr.Zero;
#endregion // Constants
public bool IsOpen {
get { return (_hMap != NULL_HANDLE); }
}
///
/// Default constructor
///
private MemoryMappedFile()
{
}
///
/// Finalizer
///
~MemoryMappedFile()
{
Dispose(false);
}
#region Create Overloads
///
/// Create an unnamed map object with no file backing
///
/// desired access to the
/// mapping object
/// maximum size of filemap object
/// name of file mapping object
/// The memory mapped file instance
public static MemoryMappedFile
Create(MapProtection protection, long maxSize, string name)
{
return Create ( null, protection, maxSize, name );
}
///
/// Create an named map object with no file backing
///
/// desired access to the
/// mapping object
/// maximum size of filemap object
/// The memory mapped file instance
public static MemoryMappedFile
Create(MapProtection protection, long maxSize)
{
return Create ( null, protection, maxSize, null );
}
///
/// Create an unnamed map object with a maximum size
/// equal to that of the file
///
/// name of backing file
/// desired access to the
/// mapping object
/// The memory mapped file instance
public static MemoryMappedFile
Create(string fileName, MapProtection protection)
{
return Create ( fileName, protection, -1, null );
}
///
/// Create an unnamed map object
///
/// name of backing file
/// desired access to the
/// mapping object
/// maximum size of filemap
/// object, or -1 for size of file
/// The memory mapped file instance
public static MemoryMappedFile
Create ( string fileName, MapProtection protection,
long maxSize )
{
return Create ( fileName, protection, maxSize, null );
}
///
/// Create a named map object
///
/// name of backing file, or null
/// for a pagefile-backed map
/// desired access to the mapping
/// object
/// maximum size of filemap object, or 0
/// for size of file
/// name of file mapping object
/// The memory mapped file instance
public static MemoryMappedFile
Create ( string fileName, MapProtection protection,
long maxSize, String name )
{
MemoryMappedFile map = new MemoryMappedFile();
// open file first
IntPtr hFile = INVALID_HANDLE_VALUE;
if ( fileName != null )
{
// determine file access needed
// we'll always need generic read access
int desiredAccess = GENERIC_READ;
if ( (protection == MapProtection.PageReadWrite) ||
(protection == MapProtection.PageWriteCopy) )
{
desiredAccess |= GENERIC_WRITE;
}
// open or create the file
// if it doesn't exist, it gets created
hFile = Win32MapApis.CreateFile (
fileName, desiredAccess, 0,
IntPtr.Zero, OPEN_ALWAYS, 0, IntPtr.Zero
);
if ( hFile == INVALID_HANDLE_VALUE )
throw new FileMapIOException ( Marshal.GetHRForLastWin32Error() );
// FIX: Is support neede for zero-length files!?!
}
map._hMap = Win32MapApis.CreateFileMapping (
hFile, IntPtr.Zero, (int)protection,
(int)((maxSize >> 32) & 0xFFFFFFFF),
(int)(maxSize & 0xFFFFFFFF), name
);
// close file handle, we don't need it
if ( hFile != INVALID_HANDLE_VALUE ) Win32MapApis.CloseHandle(hFile);
if ( map._hMap == NULL_HANDLE )
throw new FileMapIOException ( Marshal.GetHRForLastWin32Error() );
return map;
}
#endregion // Create Overloads
///
/// Open an existing named File Mapping object
///
/// desired access to the map
/// name of object
/// The memory mapped file instance
public static MemoryMappedFile Open ( MapAccess access, String name )
{
MemoryMappedFile map = new MemoryMappedFile();
map._hMap = Win32MapApis.OpenFileMapping ( (int)access, false, name );
if ( map._hMap == NULL_HANDLE )
throw new FileMapIOException ( Marshal.GetHRForLastWin32Error() );
return map;
}
///
/// Close this File Mapping object
/// From here on, You can't do anything with it
/// but the open views remain valid.
///
public void Close()
{
Dispose(true);
}
///
/// Map a view of the file mapping object
/// This returns a stream, giving you easy access to the memory,
/// as you can use StreamReaders and StreamWriters on top of it
///
/// desired access to the view
/// offset of the file mapping object to
/// start view at
/// size of the view
public Stream MapView ( MapAccess access, long offset, int size )
{
if ( !IsOpen )
throw new ObjectDisposedException("MMF already closed");
IntPtr baseAddress = IntPtr.Zero;
baseAddress = Win32MapApis.MapViewOfFile (
_hMap, (int)access,
(int)((offset >> 32) & 0xFFFFFFFF),
(int)(offset & 0xFFFFFFFF), size
);
if ( baseAddress == IntPtr.Zero )
throw new FileMapIOException ( Marshal.GetHRForLastWin32Error() );
// Find out what MapProtection to use
// based on the MapAccess flags...
MapProtection protection;
if ( access == MapAccess.FileMapRead )
protection = MapProtection.PageReadOnly;
else
protection = MapProtection.PageReadWrite;
return new MapViewStream ( baseAddress, size, protection );
}
#region IDisposable implementation
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if ( IsOpen )
Win32MapApis.CloseHandle ( _hMap );
_hMap = NULL_HANDLE;
if ( disposing )
GC.SuppressFinalize(this);
}
#endregion // IDisposable implementation
} // class MemoryMappedFile
} // namespace Winterdom.IO.FileMap