Chapter 15 - Streams and IO

Create and Write

// Create a file called test.txt in the current directory:
using (Stream s = new FileStream ("test.txt", FileMode.Create))
{
  Console.WriteLine (s.CanRead);       // True
  Console.WriteLine (s.CanWrite);      // True
  Console.WriteLine (s.CanSeek);       // True

  s.WriteByte (101);
  s.WriteByte (102);
  byte[] block = { 1, 2, 3, 4, 5 };
  s.Write (block, 0, block.Length);     // Write block of 5 bytes

  Console.WriteLine (s.Length);         // 7
  Console.WriteLine (s.Position);       // 7
  s.Position = 0;                       // Move back to the start

  Console.WriteLine (s.ReadByte());     // 101
  Console.WriteLine (s.ReadByte());     // 102

  // Read from the stream back into the block array:
  Console.WriteLine (s.Read (block, 0, block.Length));   // 5

  // Assuming the last Read returned 5, we'll be at
  // the end of the file, so Read will now return 0:
  Console.WriteLine (s.Read (block, 0, block.Length));   // 0
}

Async Demo

using (Stream s = new FileStream ("test.txt", FileMode.Create))
{
  byte[] block = { 1, 2, 3, 4, 5 };

  await s.WriteAsync (block, 0, block.Length);    // Write asychronously

  s.Position = 0;                       // Move back to the start

  // Read from the stream back into the block array:
  Console.WriteLine (await s.ReadAsync (block, 0, block.Length));   // 5
}

// Clean up
File.Delete ("test.txt");

Named Pipe Server

bool messageMode = true; // Be sure Server and Client set the same

if (messageMode)
{
  using var s = new NamedPipeServerStream ("pipedream", PipeDirection.InOut,
                                              1, PipeTransmissionMode.Message);

  s.WaitForConnection();

  byte[] msg = Encoding.UTF8.GetBytes ("Hello");
  s.Write (msg, 0, msg.Length);

  Console.WriteLine (Encoding.UTF8.GetString (ReadMessage (s)));
}
else
{
  using var s = new NamedPipeServerStream ("pipedream");

  Console.WriteLine ("Please start Named Pipe Client.");
  s.WaitForConnection();
  s.WriteByte (100);                // Send the value 100.
  Console.WriteLine ("Response from Named Pipe Client.");
  Console.WriteLine (s.ReadByte());
}

static byte[] ReadMessage (PipeStream s)
{
  MemoryStream ms = new MemoryStream();
  byte[] buffer = new byte [0x1000];      // Read in 4 KB blocks

  do { ms.Write (buffer, 0, s.Read (buffer, 0, buffer.Length)); }
  while (!s.IsMessageComplete);

  return ms.ToArray();
}

Named Pipe Client

bool messageMode = true; // Be sure Server and Client set the same

if (messageMode)
{
  using var s = new NamedPipeClientStream ("pipedream");

  s.Connect();
  s.ReadMode = PipeTransmissionMode.Message;

  Console.WriteLine (Encoding.UTF8.GetString (ReadMessage (s)));

  byte[] msg = Encoding.UTF8.GetBytes ("Hello right back!");
  s.Write (msg, 0, msg.Length);
}
else
{
  using var s = new NamedPipeClientStream ("pipedream");

  s.Connect();
  Console.WriteLine (s.ReadByte());
  s.WriteByte (200);                 // Send the value 200 back.
}

static byte[] ReadMessage (PipeStream s)
{
  MemoryStream ms = new MemoryStream();
  byte[] buffer = new byte [0x1000];      // Read in 4 KB blocks

  do { ms.Write (buffer, 0, s.Read (buffer, 0, buffer.Length)); }
  while (!s.IsMessageComplete);

  return ms.ToArray();
}

Stream Reader and Writer

using (FileStream fs = File.Create ("test.txt"))
using (TextWriter writer = new StreamWriter (fs))
{
  var nl = string.Join ("", writer.NewLine.Select (c => $"0x{((int)c).ToString("X2")} "));
  Console.WriteLine ($"Newline is {nl} ");
  
  writer.WriteLine ("Line1");
  writer.WriteLine ("Line2");
}

using (FileStream fs = File.OpenRead ("test.txt"))
using (TextReader reader = new StreamReader (fs))
{
  Console.WriteLine (reader.ReadLine());       // Line1
  Console.WriteLine (reader.ReadLine());       // Line2
}

Encoding

Console.WriteLine ("Default UTF-8 encoding");

using (TextWriter w = File.CreateText ("but.txt"))    // Use default UTF-8 encoding.
  w.WriteLine ("but–");                               // Emdash, not the "minus" character

using (Stream s = File.OpenRead ("but.txt"))
  for (int b; (b = s.ReadByte()) > -1;)
    Console.WriteLine (b);

Console.WriteLine("Unicode a.k.a. UTF-16 encoding");

using (Stream s = File.Create ("but.txt"))
using (TextWriter w = new StreamWriter (s, Encoding.Unicode))
  w.WriteLine ("but–");

foreach (byte b in File.ReadAllBytes ("but.txt"))
  Console.WriteLine (b);

Compression Streams

using (Stream s = File.Create ("compressed.bin"))
using (Stream ds = new DeflateStream (s, CompressionMode.Compress))
  for (byte i = 0; i < 100; i++)
    ds.WriteByte (i);
    
new FileInfo ("compressed.bin").Length.Dump ("Length of compressed file");

using (Stream s = File.OpenRead ("compressed.bin"))
using (Stream ds = new DeflateStream (s, CompressionMode.Decompress))
  for (byte i = 0; i < 100; i++)
    Console.WriteLine (ds.ReadByte());     // Writes 0 to 99

Compressing Text

string[] words = "The quick brown fox jumps over the lazy dog".Split();
Random rand = new Random(0);

using (Stream s = File.Create ("compressed.bin"))
using (Stream ds = new BrotliStream (s, CompressionMode.Compress))
using (TextWriter w = new StreamWriter (ds))
  for (int i = 0; i < 1000; i++)
    await w.WriteAsync (words [rand.Next (words.Length)] + " ");

new FileInfo ("compressed.bin").Length.Dump ("Length of compressed file");

using (Stream s = File.OpenRead ("compressed.bin"))
using (Stream ds = new BrotliStream (s, CompressionMode.Decompress))
using (TextReader r = new StreamReader (ds))
  Console.Write (await r.ReadToEndAsync());  // Output below:

Compressing in Memory

byte[] data = new byte [1000];          // We can expect a good compression
                                         // ratio from an empty array!
var ms = new MemoryStream();
using (Stream ds = new DeflateStream (ms, CompressionMode.Compress))
  ds.Write (data, 0, data.Length);

byte[] compressed = ms.ToArray();
Console.WriteLine (compressed.Length);       // 11

// Decompress back to the data array:
ms = new MemoryStream (compressed);
using (Stream ds = new DeflateStream (ms, CompressionMode.Decompress))
  for (int i = 0; i < 1000; i += ds.Read (data, i, 1000 - i)) ;

Compressing in Memory - alternative

byte[] data = new byte[1000];

MemoryStream ms = new MemoryStream();
using (Stream ds = new DeflateStream (ms, CompressionMode.Compress, true))
  await ds.WriteAsync (data, 0, data.Length);

Console.WriteLine (ms.Length);             // 113
ms.Position = 0;
using (Stream ds = new DeflateStream (ms, CompressionMode.Decompress))
  for (int i = 0; i < 1000; i += await ds.ReadAsync (data, i, 1000 - i)) ;

GZip Stream

async Task Main()
{
  string textfile = "myfile.txt";
  File.WriteAllText (textfile, RandomString (4096));
  var backup = textfile.Replace (".txt", "-Copy.txt");
  File.Copy (textfile, backup);

  await GZip (textfile);
  await GUnzip (textfile + ".gz", false); // Don't delete it so we can list it for comparison
 
  foreach (var fi in Directory
    .EnumerateFiles (Environment.CurrentDirectory, "myfile*")
    .Select (f => new FileInfo (f)))
    Console.WriteLine ($"{fi.Name} {fi.Length} bytes");

  File.Delete (textfile);
  File.Delete (textfile + ".gz");
  File.Delete (backup);
}

async Task GZip (string sourcefile, bool deleteSource = true)
{
  var gzipfile = $"{sourcefile}.gz";
  if (File.Exists (gzipfile)) throw new Exception ("Gzip file already exists");

  // Compress
  using (FileStream inStream = File.Open (sourcefile, FileMode.Open))
  using (FileStream outStream = new FileStream (gzipfile, FileMode.CreateNew))
  using (GZipStream gzipStream = new GZipStream (outStream, CompressionMode.Compress))
    await inStream.CopyToAsync (gzipStream); // Or .CopyTo() for non-async code

  if (deleteSource) File.Delete (sourcefile);
}

async Task GUnzip (string gzipfile, bool deleteGzip = true)
{
  if (Path.GetExtension (gzipfile) != ".gz") throw new Exception ("Not a gzip file");
  var uncompressedFile = gzipfile.Substring (0, gzipfile.Length - 3);
  if (File.Exists (uncompressedFile)) throw new Exception ("Destination file already exists");

  // Uncompress
  using (FileStream uncompressToStream = File.Open (uncompressedFile, FileMode.Create))
  using (FileStream zipfileStream = File.Open (gzipfile, FileMode.Open))
  using (var unzipStream = new GZipStream (zipfileStream, CompressionMode.Decompress))
    await unzipStream.CopyToAsync (uncompressToStream); // Or .CopyTo() for non-async code

  if (deleteGzip) File.Delete (gzipfile);
}

private static Random rnd = new Random();

// https://stackoverflow.com/a/1344242/141172
public static string RandomString (int length)
{
  const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  return new string (Enumerable.Repeat (chars, length)
    .Select (s => s [rnd.Next (s.Length)]).ToArray());
}

Zip

void Main()
{
  AddFilesToFolder();
  
  var zipPath = Path.Combine(DirectoryToZip, "..", "archive.zip");
  ZipFile.CreateFromDirectory (DirectoryToZip, zipPath);
  
  Directory.CreateDirectory(DirectoryToExtractTo);
  ZipFile.ExtractToDirectory (zipPath, DirectoryToExtractTo);
  
  Console.WriteLine("Extracted files:");
  foreach (var file in Directory.EnumerateFiles(DirectoryToExtractTo))
    Console.WriteLine(file);
}

string DirectoryToZip
{
  get
  {
    return RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ?
      @".\MyFolder" : "./MyFolder";
  }
}

string DirectoryToExtractTo
{
  get
  {
    return RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ?
      @".\Extracted" : "./Extracted";
  }
}

void AddFilesToFolder()
{
  Directory.CreateDirectory (DirectoryToZip);
  foreach (var c in new char[] { 'A', 'B', 'C' })
    File.WriteAllText (Path.Combine (DirectoryToZip, $"{c}.txt"), $"This is {c}");
}

File Attributes

string filePath = @"test.txt";
File.WriteAllText(filePath, "The quick brown fox.");

FileAttributes fa = File.GetAttributes (filePath);
if ((fa & FileAttributes.ReadOnly) != 0)
{
  // Use the exclusive-or operator (^) to toggle the ReadOnly flag
  fa ^= FileAttributes.ReadOnly;
  File.SetAttributes (filePath, fa);
}

Console.WriteLine (new FileInfo ("test.txt").IsReadOnly);
new FileInfo ("test.txt").IsReadOnly = true;
Console.WriteLine (new FileInfo ("test.txt").IsReadOnly);
new FileInfo ("test.txt").IsReadOnly = false;
Console.WriteLine (new FileInfo ("test.txt").IsReadOnly);


// Now we can delete the file, for instance:
File.Delete (filePath);

CompressFolder

void Main()
{
  CreateCompressStructure();

  CompressFolder (DirectoryToCompress, true);
  
  CleanupCompressStructure();
}

static void CreateCompressStructure()
{
  Directory.CreateDirectory (DirectoryToCompress);
  File.WriteAllText (Path.Combine (DirectoryToCompress, "MyFile.txt"), "C# is fun!");
  var subfolder = Path.Combine (DirectoryToCompress, "Subfolder");
  Directory.CreateDirectory (subfolder);
  File.WriteAllText (Path.Combine (subfolder, "FileInSubfolder.txt"), ".NET rocks!");
}

static void CleanupCompressStructure()
{
  var subfolder = Path.Combine (DirectoryToCompress, "Subfolder");
  File.Delete(Path.Combine (subfolder, "FileInSubfolder.txt"));
  Directory.Delete(subfolder);
  
  File.Delete(Path.Combine (DirectoryToCompress, "MyFile.txt"));
  Directory.Delete(DirectoryToCompress);
}

static uint CompressFolder (string folder, bool recursive)
{
  string path = "Win32_Directory.Name='" + folder + "'";

  using (ManagementObject dir = new ManagementObject (path))
  using (ManagementBaseObject p = dir.GetMethodParameters ("CompressEx"))
  {
    p ["Recursive"] = recursive;
    using (ManagementBaseObject result = dir.InvokeMethod ("CompressEx",
                                                                 p, null))
      return (uint)result.Properties ["ReturnValue"].Value;
  }
}

static string DirectoryToCompress
{
  get
  {
    return RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ?
      @"C:\Temp\CompressMe" : "/tmp/CompressMe"; // Requires a path outside LINQPad's current directory
  }
}

Enumerate File Security

try
{
  File.WriteAllText("sectest.txt", "File for testing security.");

  FileSecurity fSecurity = new FileSecurity("sectest.txt", AccessControlSections.Owner |
                                                             AccessControlSections.Group |
                                                             AccessControlSections.Access);

  Console.WriteLine (string.Join (Environment.NewLine, 
        fSecurity.GetAccessRules (true, true, typeof (NTAccount))
        .Cast<AuthorizationRule>()
        .Select (r => r.IdentityReference.Value)));
}
catch (PlatformNotSupportedException ex)
{
  Console.WriteLine ($"Not supported: {ex.Message}");
}
finally
{
  File.Delete ("sectest.txt");
}

Modify Access Control

void ShowSecurity (FileSecurity sec)
{
  AuthorizationRuleCollection rules = sec.GetAccessRules (true, true,
                                                         typeof (NTAccount));
  foreach (FileSystemAccessRule r in rules.Cast<FileSystemAccessRule>()
    .OrderBy (rule => rule.IdentityReference.Value))
  {
    // e.g., MyDomain/Joe
    Console.WriteLine ($"  {r.IdentityReference.Value}");
    // Allow or Deny: e.g., FullControl
    Console.WriteLine ($"    {r.FileSystemRights}: {r.AccessControlType}");
  }
}

var file = "sectest.txt";
File.WriteAllText (file, "File security.");

var sid = new SecurityIdentifier (WellKnownSidType.BuiltinUsersSid, null);
string usersAccount = sid.Translate (typeof (NTAccount)).ToString();

Console.WriteLine ($"User: {usersAccount}");

FileSecurity sec = new FileSecurity (file,
                          AccessControlSections.Owner |
                          AccessControlSections.Group |
                          AccessControlSections.Access);

Console.WriteLine ("AFTER CREATE:");
ShowSecurity(sec);

sec.ModifyAccessRule (AccessControlModification.Add,
    new FileSystemAccessRule (usersAccount, FileSystemRights.Write, 
                              AccessControlType.Allow),
    out bool modified);

Console.WriteLine ("AFTER MODIFY:");
ShowSecurity (sec);

File Info

void Main()
{
  Directory.CreateDirectory (TempDirectory);

  FileInfo fi = new FileInfo (Path.Combine(TempDirectory, "FileInfo.txt"));

  Console.WriteLine (fi.Exists);         // false

  using (TextWriter w = fi.CreateText())
    w.Write ("Some text");

  Console.WriteLine (fi.Exists);         // false (still)
  fi.Refresh();
  Console.WriteLine (fi.Exists);         // true

  Console.WriteLine (fi.Name);           // FileInfo.txt
  Console.WriteLine (fi.FullName);       // c:\temp\FileInfo.txt (Windows)
                                           // /tmp/FileInfo.txt (Unix)
  Console.WriteLine (fi.DirectoryName);  // c:\temp (Windows)
                                           // /tmp (Unix)
  Console.WriteLine (fi.Directory.Name); // temp
  Console.WriteLine (fi.Extension);      // .txt
  Console.WriteLine (fi.Length);         // 9

  fi.Encrypt();
  fi.Attributes ^= FileAttributes.Hidden;   // (Toggle hidden flag)
  fi.IsReadOnly = true;

  Console.WriteLine (fi.Attributes);    // ReadOnly,Archive,Hidden,Encrypted
  Console.WriteLine (fi.CreationTime);  // 3/09/2019 1:24:05 PM

  fi.MoveTo (Path.Combine(TempDirectory, "FileInfoX.txt")); 

  DirectoryInfo di = fi.Directory;
  Console.WriteLine (di.Name);             // temp or tmp
  Console.WriteLine (di.FullName);         // c:\temp or /tmp
  Console.WriteLine (di.Parent.FullName);  // c:\
  di.CreateSubdirectory ("SubFolder");

  Cleanup();
}

static string TempDirectory
{
  get
  {
    return RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ?
      @"C:\Temp" : "/tmp"; 
  }
}
// Clean up
static void Cleanup()
{
  var subfolder = Path.Combine(TempDirectory, "SubFolder");
  if (Directory.Exists(subfolder)) Directory.Delete (subfolder);
  var fi = new FileInfo (Path.Combine(TempDirectory, "FileInfo.txt"));
  if (fi.Exists)
  {
    fi.Attributes &= ~FileAttributes.Hidden;   // (Toggle hidden flag)
    fi.IsReadOnly = false;
    fi.Delete();
  }
  var fiX = new FileInfo (Path.Combine(TempDirectory, "FileInfoX.txt"));
  if (fiX.Exists)
  {
    fiX.Attributes &= ~FileAttributes.Hidden;   // (Toggle hidden flag)
    fiX.IsReadOnly = false;
    fiX.Delete();
  }
}

Special Folders

foreach (var val in Enum.GetValues(typeof(Environment.SpecialFolder)).Cast<Environment.SpecialFolder>().Distinct().OrderBy(v =>v.ToString()))
{
  $"{Environment.GetFolderPath (val)}".Dump(val.ToString());
}

Drive Information

DriveInfo c = new DriveInfo ("C");       // Query the C: drive.

long totalSize = c.TotalSize;            // Size in bytes.
long freeBytes = c.TotalFreeSpace;       // Ignores disk quotas.
long freeToMe = c.AvailableFreeSpace;   // Takes quotas into account.

foreach (DriveInfo d in DriveInfo.GetDrives().OrderBy(d => d.Name))    // All defined drives.
{
  Console.WriteLine (d.Name);             // C:\
  Console.WriteLine (d.DriveType);        // Fixed
  Console.WriteLine (d.RootDirectory);    // C:\

  if (d.IsReady)   // If the drive is not ready, the following two
                     // properties will throw exceptions:
  {
    Console.WriteLine (d.VolumeLabel);    // The Sea Drive
    Console.WriteLine (d.DriveFormat);    // NTFS
  }
  Console.WriteLine();
}

File Watcher

Watch (GetTestDirectory(), "*.txt", true);

void Watch (string path, string filter, bool includeSubDirs)
{
  using (var watcher = new FileSystemWatcher (path, filter))
  {
    watcher.Created += FileCreatedChangedDeleted;
    watcher.Changed += FileCreatedChangedDeleted;
    watcher.Deleted += FileCreatedChangedDeleted;
    watcher.Renamed += FileRenamed;
    watcher.Error   += FileError;

    watcher.IncludeSubdirectories = includeSubDirs;
    watcher.EnableRaisingEvents = true;

    Console.WriteLine ("Listening for events - press <enter> to end");
    Console.ReadLine();
  }
  // Disposing the FileSystemWatcher stops further events from firing.
}

void FileCreatedChangedDeleted (object o, FileSystemEventArgs e)
  => Console.WriteLine ("File {0} has been {1}", e.FullPath, e.ChangeType);

void FileRenamed (object o, RenamedEventArgs e)
  => Console.WriteLine ("Renamed: {0}->{1}", e.OldFullPath, e.FullPath);

void FileError (object o, ErrorEventArgs e)
  => Console.WriteLine ("Error: " + e.GetException().Message);

string GetTestDirectory() =>
  RuntimeInformation.IsOSPlatform (OSPlatform.Windows)
    ? @"C:\Temp"
    : "/tmp";

Get Volume Information

class SupportsCompressionEncryption
{
  const int SupportsCompression = 0x10;
  const int SupportsEncryption = 0x20000;

  [DllImport ("Kernel32.dll", SetLastError = true)]
  extern static bool GetVolumeInformation (string vol, StringBuilder name,
    int nameSize, out uint serialNum, out uint maxNameLen, out uint flags,
    StringBuilder fileSysName, int fileSysNameSize);

  static void Main()
  {
    string volume = @"C:\";
    uint serialNum, maxNameLen, flags;
    bool ok = GetVolumeInformation (volume, null, 0, out serialNum,
                                        out maxNameLen, out flags, null, 0);
    if (!ok)
      throw new Win32Exception();

    bool canCompress = (flags & SupportsCompression) != 0;
    bool canEncrypt = (flags & SupportsEncryption) != 0;

    Console.WriteLine ($"Volume {volume} supports compression: {canCompress}; encryption: {canEncrypt}");
    
  }
}

Memory Mapped

File.WriteAllBytes ("long.bin", new byte [1000000]);

using MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile ("long.bin");
using MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor();
accessor.Write (500000, (byte)77);
Console.WriteLine (accessor.ReadByte (500000));   // 77

File.WriteAllBytes ("short.bin", new byte [1]);
using MemoryMappedFile mmfSh = MemoryMappedFile.CreateFromFile
                   ("short.bin", FileMode.Create, null, 1000);
using MemoryMappedViewAccessor accessorSh = mmfSh.CreateViewAccessor();

accessorSh.Write (500, (byte)42);
Console.WriteLine (accessorSh.ReadByte (500));

Memory Mapped Named Reader

// This can run in a separate executable:
using MemoryMappedFile mmFile = MemoryMappedFile.OpenExisting ("Demo");
using MemoryMappedViewAccessor accessor = mmFile.CreateViewAccessor();

Console.WriteLine (accessor.ReadInt32 (0));   // 12345

Memory Mapped Named Writer

using MemoryMappedFile mmFile = MemoryMappedFile.CreateNew ("Demo", 500);
using MemoryMappedViewAccessor accessor = mmFile.CreateViewAccessor();

accessor.Write (0, 12345);
  
// In LINQPad, manaully stop the query to exit
Console.ReadLine();   // Keep shared memory alive until user hits Enter.

Memory Mapped Reader

void Main()
{
  // This can run in a separate executable:
  var file = Path.Combine (TempDirectory, "interprocess.bin");
  using FileStream fs = new FileStream (file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
  using MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile (fs, null, fs.Length, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, true);
  using MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor();
  
  Console.WriteLine (accessor.ReadInt32 (0));   // 12345
}

static string TempDirectory
{
  get => RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ?
            @"C:\Temp" : "/tmp";
}

Memory Mapped Writer

void Main()
{
  var file = Path.Combine(TempDirectory, "interprocess.bin");
  File.WriteAllBytes (file, new byte [100]);
  
  using FileStream fs = new FileStream (file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
  using MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile (fs, null, fs.Length, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, true);
  using MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor();
    
  accessor.Write (0, 12345);
  
  // In LINQPad, manaully stop the query to exit
  Console.ReadLine();   // Keep shared memory alive until user hits Enter.
 
  File.Delete(file);
}

static string TempDirectory
{
  get => RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ?
            @"C:\Temp" : "/tmp";
}

Memory Mapped Server

using (MemoryMappedFile mmFile = MemoryMappedFile.CreateNew ("Demo", 500))
using (MemoryMappedViewAccessor accessor = mmFile.CreateViewAccessor())
{
  accessor.Write (0, 12345);
  
  // In LINQPad, manaully stop the query to exit
  Console.ReadLine();   // Keep shared memory alive until user hits Enter.
}

Memory Mapped Unsafe

void Main()
{
  File.WriteAllBytes ("unsafe.bin", new byte [100]);

  var data = new Data { X = 123, Y = 456 };

  using MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile ("unsafe.bin");
  using MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor();

  accessor.Write (0, ref data);
  accessor.Read (0, out data);
  Console.WriteLine (data.X + " " + data.Y);   // 123 456

  unsafe
  {
    byte* pointer = null;
    try
    {
      accessor.SafeMemoryMappedViewHandle.AcquirePointer (ref pointer);
      int* intPointer = (int*)pointer;
      Console.WriteLine (*intPointer);               // 123
    }
    finally
    {
      if (pointer != null)
        accessor.SafeMemoryMappedViewHandle.ReleasePointer();
    }
  }
}

struct Data { public int X, Y; }
C# 12 in a Nutshell
Buy from amazon.com Buy print or Kindle edition
Buy from ebooks.com Buy PDF edition
Buy from O'Reilly Read via O'Reilly subscription