// // 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; using DTS.Common.Utilities.Logging; namespace DTS.Common.Utilities.IO.MemoryMap { /// /// 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(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 => (_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) { var map = new MemoryMappedFile(); // open file first var hFile = INVALID_HANDLE_VALUE; if (fileName != null) { // determine file access needed // we'll always need generic read access var 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 needed 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) { try { var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); APILogger.Log("Create(", fileName, ") failed, null handle, ", w32.Message); } catch { } var ex = Win32MapApis.GetLastError(); 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) { var 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"); var baseAddress = IntPtr.Zero; baseAddress = Win32MapApis.MapViewOfFile( _hMap, (int)access, (int)((offset >> 32) & 0xFFFFFFFF), (int)(offset & 0xFFFFFFFF), size ); if (baseAddress == IntPtr.Zero) { try { var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); APILogger.Log("MapView - error, base address= 0, ", w32.Message); } catch { } var ex = Win32MapApis.GetLastError(); 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 }