public static unsafe MemoryMappedView CreateView(SafeMemoryMappedFileHandle memMappedFileHandle,
MemoryMappedFileAccess access, long offset, long size)
{
// MapViewOfFile can only create views that start at a multiple of the system memory allocation
// granularity. We decided to hide this restriction from the user by creating larger views than the
// user requested and hiding the parts that the user did not request. extraMemNeeded is the amount of
// extra memory we allocate before the start of the requested view. MapViewOfFile will also round the
// capacity of the view to the nearest multiple of the system page size. Once again, we hide this
// from the user by preventing them from writing to any memory that they did not request.
ulong nativeSize;
long extraMemNeeded, newOffset;
ValidateSizeAndOffset(
size, offset, GetSystemPageAllocationGranularity(),
out nativeSize, out extraMemNeeded, out newOffset);
// if request is >= than total virtual, then MapViewOfFile will fail with meaningless error message
// "the parameter is incorrect"; this provides better error message in advance
Interop.CheckForAvailableVirtualMemory(nativeSize);
// create the view
SafeMemoryMappedViewHandle viewHandle = Interop.MapViewOfFile(memMappedFileHandle,
(int)MemoryMappedFile.GetFileMapAccess(access), newOffset, new UIntPtr(nativeSize));
if (viewHandle.IsInvalid)
{
viewHandle.Dispose();
throw Win32Marshal.GetExceptionForLastWin32Error();
}
// Query the view for its size and allocation type
Interop.Kernel32.MEMORY_BASIC_INFORMATION viewInfo = new Interop.Kernel32.MEMORY_BASIC_INFORMATION();
Interop.Kernel32.VirtualQuery(viewHandle, ref viewInfo, (UIntPtr)Marshal.SizeOf(viewInfo));
ulong viewSize = (ulong)viewInfo.RegionSize;
// Allocate the pages if we were using the MemoryMappedFileOptions.DelayAllocatePages option
// OR check if the allocated view size is smaller than the expected native size
// If multiple overlapping views are created over the file mapping object, the pages in a given region
// could have different attributes(MEM_RESERVE OR MEM_COMMIT) as MapViewOfFile preserves coherence between
// views created on a mapping object backed by same file.
// In which case, the viewSize will be smaller than nativeSize required and viewState could be MEM_COMMIT
// but more pages may need to be committed in the region.
// This is because, VirtualQuery function(that internally invokes VirtualQueryEx function) returns the attributes
// and size of the region of pages with matching attributes starting from base address.
// VirtualQueryEx: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366907(v=vs.85).aspx
if (((viewInfo.State & Interop.Kernel32.MemOptions.MEM_RESERVE) != 0) || ((ulong)viewSize < (ulong)nativeSize))
{
IntPtr tempHandle = Interop.VirtualAlloc(
viewHandle, (UIntPtr)(nativeSize != MemoryMappedFile.DefaultSize ? nativeSize : viewSize),
Interop.Kernel32.MemOptions.MEM_COMMIT, MemoryMappedFile.GetPageAccess(access));
int lastError = Marshal.GetLastWin32Error();
if (viewHandle.IsInvalid)
{
viewHandle.Dispose();
throw Win32Marshal.GetExceptionForWin32Error(lastError);
}
// again query the view for its new size
viewInfo = new Interop.Kernel32.MEMORY_BASIC_INFORMATION();
Interop.Kernel32.VirtualQuery(viewHandle, ref viewInfo, (UIntPtr)Marshal.SizeOf(viewInfo));
viewSize = (ulong)viewInfo.RegionSize;
}
// if the user specified DefaultSize as the size, we need to get the actual size
if (size == MemoryMappedFile.DefaultSize)
{
size = (long)(viewSize - (ulong)extraMemNeeded);
}
else
{
Debug.Assert(viewSize >= (ulong)size, "viewSize < size");
}
viewHandle.Initialize((ulong)size + (ulong)extraMemNeeded);
return(new MemoryMappedView(viewHandle, extraMemNeeded, size, access));
}