diff --git a/src/libraries/System.IO.MemoryMappedFiles/ref/System.IO.MemoryMappedFiles.cs b/src/libraries/System.IO.MemoryMappedFiles/ref/System.IO.MemoryMappedFiles.cs
index 2182c4a6d5b5a..c5b786f48acdf 100644
--- a/src/libraries/System.IO.MemoryMappedFiles/ref/System.IO.MemoryMappedFiles.cs
+++ b/src/libraries/System.IO.MemoryMappedFiles/ref/System.IO.MemoryMappedFiles.cs
@@ -25,6 +25,7 @@ public partial class MemoryMappedFile : System.IDisposable
internal MemoryMappedFile() { }
public Microsoft.Win32.SafeHandles.SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle { get { throw null; } }
public static System.IO.MemoryMappedFiles.MemoryMappedFile CreateFromFile(System.IO.FileStream fileStream, string? mapName, long capacity, System.IO.MemoryMappedFiles.MemoryMappedFileAccess access, System.IO.HandleInheritability inheritability, bool leaveOpen) { throw null; }
+ public static System.IO.MemoryMappedFiles.MemoryMappedFile CreateFromFile(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, string? mapName, long capacity, System.IO.MemoryMappedFiles.MemoryMappedFileAccess access, System.IO.HandleInheritability inheritability, bool leaveOpen) { throw null; }
public static System.IO.MemoryMappedFiles.MemoryMappedFile CreateFromFile(string path) { throw null; }
public static System.IO.MemoryMappedFiles.MemoryMappedFile CreateFromFile(string path, System.IO.FileMode mode) { throw null; }
public static System.IO.MemoryMappedFiles.MemoryMappedFile CreateFromFile(string path, System.IO.FileMode mode, string? mapName) { throw null; }
diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs
index 57dc662749335..49b33555f120d 100644
--- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs
+++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs
@@ -107,22 +107,7 @@ public static MemoryMappedFile CreateFromFile(string path, FileMode mode, string
MemoryMappedFileAccess access)
{
ArgumentNullException.ThrowIfNull(path);
-
- if (mapName != null && mapName.Length == 0)
- {
- throw new ArgumentException(SR.Argument_MapNameEmptyString);
- }
-
- if (capacity < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_PositiveOrDefaultCapacityRequired);
- }
-
- if (access < MemoryMappedFileAccess.ReadWrite ||
- access > MemoryMappedFileAccess.ReadWriteExecute)
- {
- throw new ArgumentOutOfRangeException(nameof(access));
- }
+ ValidateCreateFile(mapName, capacity, access);
if (mode == FileMode.Append)
{
@@ -132,10 +117,6 @@ public static MemoryMappedFile CreateFromFile(string path, FileMode mode, string
{
throw new ArgumentException(SR.Argument_NewMMFTruncateModeNotAllowed, nameof(mode));
}
- if (access == MemoryMappedFileAccess.Write)
- {
- throw new ArgumentException(SR.Argument_NewMMFWriteAccessNotAllowed, nameof(access));
- }
bool existed = mode switch
{
@@ -186,37 +167,84 @@ public static MemoryMappedFile CreateFromFile(string path, FileMode mode, string
return new MemoryMappedFile(handle, fileHandle, false);
}
- public static MemoryMappedFile CreateFromFile(FileStream fileStream, string? mapName, long capacity,
+ ///
+ /// Creates a memory-mapped file from an existing file using a ,
+ /// and the specified access mode, name, inheritability, and capacity.
+ ///
+ /// The to the existing file. Caller is
+ /// responsible for disposing when is (otherwise,
+ /// automatically disposed by the ).
+ /// A name to assign to the memory-mapped file, or for a
+ /// that you do not intend to share across processes.
+ /// The maximum size, in bytes, to allocate to the memory-mapped file.
+ /// Specify 0 to set the capacity to the size of the file.
+ /// One of the enumeration values that specifies the type of access allowed
+ /// to the memory-mapped file.
+ /// This parameter can't be set to
+ /// One of the enumeration values that specifies whether a handle
+ /// to the memory-mapped file can be inherited by a child process. The default is .
+ /// A value that indicates whether to close the source file handle when
+ /// the is disposed.
+ /// A memory-mapped file that has the specified characteristics.
+ ///
+ /// is or an empty string.
+ /// -or-
+ /// and the length of the file are zero.
+ /// -or-
+ /// is set to , which is not allowed.
+ /// -or-
+ /// is set to and is larger than the length of the file.
+ ///
+ /// is .
+ ///
+ /// is less than zero.
+ /// -or-
+ /// is less than the file size.
+ /// -or-
+ /// is not a valid enumeration value.
+ /// -or-
+ /// is not a valid enumeration value.
+ ///
+ public static MemoryMappedFile CreateFromFile(SafeFileHandle fileHandle, string? mapName, long capacity,
MemoryMappedFileAccess access,
HandleInheritability inheritability, bool leaveOpen)
{
- ArgumentNullException.ThrowIfNull(fileStream);
+ ArgumentNullException.ThrowIfNull(fileHandle);
+ ValidateCreateFile(mapName, capacity, access);
- if (mapName != null && mapName.Length == 0)
+ long fileSize = RandomAccess.GetLength(fileHandle);
+ if (capacity == 0 && fileSize == 0)
{
- throw new ArgumentException(SR.Argument_MapNameEmptyString);
+ throw new ArgumentException(SR.Argument_EmptyFile);
}
- if (capacity < 0)
+ if (inheritability < HandleInheritability.None || inheritability > HandleInheritability.Inheritable)
{
- throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_PositiveOrDefaultCapacityRequired);
+ throw new ArgumentOutOfRangeException(nameof(inheritability));
}
- long fileSize = fileStream.Length;
- if (capacity == 0 && fileSize == 0)
+ if (capacity == DefaultSize)
{
- throw new ArgumentException(SR.Argument_EmptyFile);
+ capacity = fileSize;
}
- if (access < MemoryMappedFileAccess.ReadWrite ||
- access > MemoryMappedFileAccess.ReadWriteExecute)
- {
- throw new ArgumentOutOfRangeException(nameof(access));
- }
+ SafeMemoryMappedFileHandle handle = CreateCore(fileHandle, mapName, inheritability,
+ access, MemoryMappedFileOptions.None, capacity, fileSize);
- if (access == MemoryMappedFileAccess.Write)
+ return new MemoryMappedFile(handle, fileHandle, leaveOpen);
+ }
+
+ public static MemoryMappedFile CreateFromFile(FileStream fileStream, string? mapName, long capacity,
+ MemoryMappedFileAccess access,
+ HandleInheritability inheritability, bool leaveOpen)
+ {
+ ArgumentNullException.ThrowIfNull(fileStream);
+ ValidateCreateFile(mapName, capacity, access);
+
+ long fileSize = fileStream.Length;
+ if (capacity == 0 && fileSize == 0)
{
- throw new ArgumentException(SR.Argument_NewMMFWriteAccessNotAllowed, nameof(access));
+ throw new ArgumentException(SR.Argument_EmptyFile);
}
if (inheritability < HandleInheritability.None || inheritability > HandleInheritability.Inheritable)
@@ -483,5 +511,29 @@ private static void CleanupFile(SafeFileHandle fileHandle, bool existed, string
File.Delete(path);
}
}
+
+ private static void ValidateCreateFile(string? mapName, long capacity, MemoryMappedFileAccess access)
+ {
+ if (mapName != null && mapName.Length == 0)
+ {
+ throw new ArgumentException(SR.Argument_MapNameEmptyString);
+ }
+
+ if (capacity < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_PositiveOrDefaultCapacityRequired);
+ }
+
+ if (access < MemoryMappedFileAccess.ReadWrite ||
+ access > MemoryMappedFileAccess.ReadWriteExecute)
+ {
+ throw new ArgumentOutOfRangeException(nameof(access));
+ }
+
+ if (access == MemoryMappedFileAccess.Write)
+ {
+ throw new ArgumentException(SR.Argument_NewMMFWriteAccessNotAllowed, nameof(access));
+ }
+ }
}
}
diff --git a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs
index 8877936ed1287..894eaf4bc2b6d 100644
--- a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs
+++ b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs
@@ -36,7 +36,17 @@ public void InvalidArguments_Path()
public void InvalidArguments_FileStream()
{
// null is an invalid stream
- AssertExtensions.Throws("fileStream", () => MemoryMappedFile.CreateFromFile(null, CreateUniqueMapName(), 4096, MemoryMappedFileAccess.Read, HandleInheritability.None, true));
+ AssertExtensions.Throws("fileStream", () => MemoryMappedFile.CreateFromFile((FileStream)null, CreateUniqueMapName(), 4096, MemoryMappedFileAccess.Read, HandleInheritability.None, true));
+ }
+
+ ///
+ /// Tests invalid arguments to the CreateFromFile fileHandle parameter
+ ///
+ [Fact]
+ public void InvalidArguments_SafeFileHandle()
+ {
+ // null is an invalid handle
+ AssertExtensions.Throws("fileHandle", () => MemoryMappedFile.CreateFromFile((SafeFileHandle)null, CreateUniqueMapName(), 4096, MemoryMappedFileAccess.Read, HandleInheritability.None, true));
}
///
@@ -103,6 +113,18 @@ public void InvalidArguments_Access()
// Write-only access is not allowed
AssertExtensions.Throws("access", () => MemoryMappedFile.CreateFromFile(fs, CreateUniqueMapName(), 4096, MemoryMappedFileAccess.Write, HandleInheritability.None, true));
}
+
+ // Test the same things, but with a SafeFileHandle
+ using (TempFile file = new TempFile(GetTestFilePath()))
+ using (SafeFileHandle fileHandle = File.OpenHandle(file.Path, FileMode.Open, FileAccess.ReadWrite))
+ {
+ // Out of range values with a fileHandle
+ AssertExtensions.Throws("access", () => MemoryMappedFile.CreateFromFile(fileHandle, CreateUniqueMapName(), 4096, (MemoryMappedFileAccess)(-2), HandleInheritability.None, true));
+ AssertExtensions.Throws("access", () => MemoryMappedFile.CreateFromFile(fileHandle, CreateUniqueMapName(), 4096, (MemoryMappedFileAccess)(42), HandleInheritability.None, true));
+
+ // Write-only access is not allowed
+ AssertExtensions.Throws("access", () => MemoryMappedFile.CreateFromFile(fileHandle, CreateUniqueMapName(), 4096, MemoryMappedFileAccess.Write, HandleInheritability.None, true));
+ }
}
///
@@ -115,7 +137,7 @@ public void InvalidArguments_Access()
[InlineData(FileAccess.ReadWrite, MemoryMappedFileAccess.CopyOnWrite)]
[InlineData(FileAccess.Read, MemoryMappedFileAccess.Read)]
[InlineData(FileAccess.Read, MemoryMappedFileAccess.CopyOnWrite)]
- public void FileAccessAndMapAccessCombinations_Valid(FileAccess fileAccess, MemoryMappedFileAccess mmfAccess)
+ public void FileAccessAndMapAccessCombinationsWithFileStream_Valid(FileAccess fileAccess, MemoryMappedFileAccess mmfAccess)
{
const int Capacity = 4096;
using (TempFile file = new TempFile(GetTestFilePath(), Capacity))
@@ -126,6 +148,27 @@ public void FileAccessAndMapAccessCombinations_Valid(FileAccess fileAccess, Memo
}
}
+ ///
+ /// Tests various values of FileAccess used to construct a SafeFileHandle and MemoryMappedFileAccess used
+ /// to construct a map over that file handle. The combinations should all be valid.
+ ///
+ [Theory]
+ [InlineData(FileAccess.ReadWrite, MemoryMappedFileAccess.Read)]
+ [InlineData(FileAccess.ReadWrite, MemoryMappedFileAccess.ReadWrite)]
+ [InlineData(FileAccess.ReadWrite, MemoryMappedFileAccess.CopyOnWrite)]
+ [InlineData(FileAccess.Read, MemoryMappedFileAccess.Read)]
+ [InlineData(FileAccess.Read, MemoryMappedFileAccess.CopyOnWrite)]
+ public void FileAccessAndMapAccessCombinationsWithSafeFileHandle_Valid(FileAccess fileAccess, MemoryMappedFileAccess mmfAccess)
+ {
+ const int Capacity = 4096;
+ using (TempFile file = new TempFile(GetTestFilePath(), Capacity))
+ using (SafeFileHandle fileHandle = File.OpenHandle(file.Path, FileMode.Open, fileAccess))
+ using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fileHandle, null, Capacity, mmfAccess, HandleInheritability.None, true))
+ {
+ ValidateMemoryMappedFile(mmf, Capacity, mmfAccess);
+ }
+ }
+
///
/// Tests various values of FileAccess used to construct a FileStream and MemoryMappedFileAccess used
/// to construct a map over that stream on Windows. The combinations should all be invalid, resulting in exception.
@@ -197,6 +240,11 @@ public void InvalidArguments_MapName()
{
AssertExtensions.Throws(null, () => MemoryMappedFile.CreateFromFile(fs, string.Empty, 4096, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, true));
}
+
+ using (SafeFileHandle fileHandle = File.OpenHandle(file.Path, FileMode.Open, FileAccess.ReadWrite))
+ {
+ AssertExtensions.Throws(null, () => MemoryMappedFile.CreateFromFile(fileHandle, string.Empty, 4096, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, true));
+ }
}
}
@@ -269,6 +317,35 @@ public void InvalidArguments_Capacity()
}
}
+ ///
+ /// Tests invalid arguments to the CreateFromFile capacity parameter
+ /// when used with SafeFileHandle parameter.
+ ///
+ [Fact]
+ public void InvalidArguments_CapacityWithFileHandle()
+ {
+ using (TempFile file = new TempFile(GetTestFilePath()))
+ {
+ using (SafeFileHandle fileHandle = File.OpenHandle(file.Path, FileMode.Open, FileAccess.ReadWrite))
+ {
+ // Out of range values for capacity
+ Assert.Throws(() => MemoryMappedFile.CreateFromFile(fileHandle, null, -1, MemoryMappedFileAccess.Read, HandleInheritability.None, true));
+
+ // Default (0) capacity with an empty file
+ AssertExtensions.Throws(null, () => MemoryMappedFile.CreateFromFile(fileHandle, null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, true));
+ AssertExtensions.Throws(null, () => MemoryMappedFile.CreateFromFile(fileHandle, CreateUniqueMapName(), 0, MemoryMappedFileAccess.Read, HandleInheritability.None, true));
+
+ // Larger capacity than the underlying file, but read-only such that we can't expand the file
+ RandomAccess.SetLength(fileHandle, 4096);
+ AssertExtensions.Throws(null, () => MemoryMappedFile.CreateFromFile(fileHandle, null, 8192, MemoryMappedFileAccess.Read, HandleInheritability.None, true));
+ AssertExtensions.Throws(null, () => MemoryMappedFile.CreateFromFile(fileHandle, CreateUniqueMapName(), 8192, MemoryMappedFileAccess.Read, HandleInheritability.None, true));
+
+ // Capacity can't be less than the file size (for such cases a view can be created with the smaller size)
+ AssertExtensions.Throws("capacity", () => MemoryMappedFile.CreateFromFile(fileHandle, null, 1, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, true));
+ }
+ }
+ }
+
///
/// Tests invalid arguments to the CreateFromFile inheritability parameter.
///
@@ -279,10 +356,17 @@ public void InvalidArguments_Inheritability(HandleInheritability inheritability)
{
// Out of range values for inheritability
using (TempFile file = new TempFile(GetTestFilePath()))
- using (FileStream fs = File.Open(file.Path, FileMode.Open))
{
- AssertExtensions.Throws("inheritability", () => MemoryMappedFile.CreateFromFile(fs, CreateUniqueMapName(), 4096, MemoryMappedFileAccess.ReadWrite, inheritability, true));
- }
+ using (FileStream fs = File.Open(file.Path, FileMode.Open))
+ {
+ AssertExtensions.Throws("inheritability", () => MemoryMappedFile.CreateFromFile(fs, CreateUniqueMapName(), 4096, MemoryMappedFileAccess.ReadWrite, inheritability, true));
+ }
+
+ using (SafeFileHandle fileHandle = File.OpenHandle(file.Path, FileMode.Open, FileAccess.ReadWrite))
+ {
+ AssertExtensions.Throws("inheritability", () => MemoryMappedFile.CreateFromFile(fileHandle, CreateUniqueMapName(), 4096, MemoryMappedFileAccess.ReadWrite, inheritability, true));
+ }
+ }
}
///
@@ -448,11 +532,44 @@ public static IEnumerable