Click here to Skip to main content
15,881,173 members
Articles / Programming Languages / C#

Memory Mapped File Interoperability with .NET Objects

Rate me:
Please Sign up or sign in to vote.
4.76/5 (11 votes)
23 Oct 2012CPOL2 min read 50.6K   1.4K   30   9
This article provides the basic framework for reading/writing .NET objects, targeting the Windows Memory Mapped File API.

Introduction

Microsoft extended the .NET 4.0 API to include Memory Mapped Files (MMF), significantly improving .NET interoperability with other Windows architectures. Good articles and C# examples are provided by Microsoft and elsewhere demonstrating binary (byte array) I/O for inter-process communication, but little-to-nothing exists for practical use, such as, reading/writing anything other than simple data types. Reading/writing binary .NET managed objects was the challenge. 

This article provides the basic framework for reading/writing complex .NET object variables using the using .NET API found in the System.IO.MemoryMappedFiles namespace. 

Using the code

This code demonstrates simple  MMF I/O, including binary serialization between MMF byte arrays and managed .NET objects. 

So why not use XML Serializers and write ASCII? There is a time and a place for everything.

First and foremost, examples below assume:

C#
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.Serialization.Formatters.Binary;
Writing to MMF  

Let's begin with a .NET managed class used as a data container. Everything that should be serialized to MMF must be tagged as Serializable. In this case, here is the structure for a simple hiking database containing GPS tracks. 

C#
[Serializable]
public class HikingDatabase
{
    public string Description;
    public Hike[] hikes;
}
[Serializable]public class Hike
{
    public string Date;
    public string Description;
    public Coord[] GPSTrack;
}
[Serializable]
public class Coord
{
    public double x;
    public double y;
    public double z;
}   

Next, let's assume data has been collected. We now need a simple way to write the managed object to a MMF as binary to be shared with other users of this Memory Mapped File: 

C#
// Write managed object to MMF
WriteObjectToMMF("C:\\TEMP\\TEST.MMF", hikingData);  
C#
public void WriteObjectToMMF(string mmfFile, object objectData)
{
    // Convert .NET object to byte array
    byte[] buffer = ObjectToByteArray(objectData);
 
    // Create a new memory mapped file
    using (MemoryMappedFile mmf = 
           MemoryMappedFile.CreateFromFile(mmfFile, FileMode.Create, null, buffer.Length))
    {
        // Create a view accessor into the file to accommmodate binary data size
        using (MemoryMappedViewAccessor mmfWriter = mmf.CreateViewAccessor(0, buffer.Length))
        {
            // Write the data
            mmfWriter.WriteArray<byte>(0, buffer, 0, buffer.Length);
        }
    }
} 

The missing step in most MMF examples is the "ObjectToByteArray" function that converts a managed object into its binary form. 

C#
public byte[] ObjectToByteArray(object inputObject)
{
    BinaryFormatter binaryFormatter = new BinaryFormatter();    // Create new BinaryFormatter
    MemoryStream memoryStream = new MemoryStream();             // Create target memory stream
    binaryFormatter.Serialize(memoryStream, inputObject);       // Serialize object to stream
    return memoryStream.ToArray();                              // Return stream as byte array
} 

 Of course, you will want to wrap all these things with a try/catch before putting code into production.  

 Reading from MMF 

You now have a file persisting managed objects to be consumed by other processes. These other processes will need to read these same objects. Very simply, this is done by opening the MMF, reading the byte array into a buffer, and converting that buffer back into a managed object:  

C#
// Read managed object from MMF
HikingDatabase hikingData = ReadObjectFromMMF("C:\\TEMP\\TEST.MMF") as HikingDatabase; 
C#
public object ReadObjectFromMMF(string mmfFile)
{
    // Get a handle to an existing memory mapped file
    using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(mmfFile, FileMode.Open))
    {
        // Create a view accessor from which to read the data
        using (MemoryMappedViewAccessor mmfReader = mmf.CreateViewAccessor())
        {
            // Create a data buffer and read entire MMF view into buffer
            byte[] buffer = new byte[mmfReader.Capacity];
            mmfReader.ReadArray<byte>(0, buffer, 0, buffer.Length);
 
            // Convert the buffer to a .NET object
            return ByteArrayToObject(buffer);
        }
    }
} 

Again, below is the companion binary-to-managed ByteArrayToObject function. 

C#
public object ByteArrayToObject(byte[] buffer)
{ 
    BinaryFormatter binaryFormatter = new BinaryFormatter(); // Create new BinaryFormatter
    MemoryStream memoryStream = new MemoryStream(buffer);    // Convert buffer to memorystream
    return binaryFormatter.Deserialize(memoryStream);        // Deserialize stream to an object
}   

As stated before, you will want to wrap these with try/catch

Points of Interest 

I purposefully used API and avoided offsets into MMFs which support random access to multiple memory buffers. While random access is good for persisting multiple objects, it doesn't perform so well for whole-file I/O. When reading/writing whole files, I suggest using CreateViewStream in place of CreateViewAccessor

When multiple processes share an open file, then it's wise to create a named mutex to manage concurrency. Failure to do so can result in unwanted behavior that can be very difficult to debug. 

For more information, I suggest starting with the following articles from CodeProject and MSDN: 

There are many, many other good articles. All you need do is search.

History 

  • 2012-10-02 - Initial posting. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Anodyne Enterprise Security
United States United States
I've spent the last 30+ years building secure, durable code in government, healthcare, and critical infrastructure industries, from FORTRAN 4 to C#, on databases from pre-SQL ISAM ones to Oracle 12, and on platforms from VMS to Linux (not to exclude all common permutations by Microsoft). In my spare time, I teach classes on whole-system design principles from configuring SANS, programming switches, to advanced principles in C# for creating secure, durable code. I am CISSP and CCSP certified, regularly working with legal teams to validate Cloud security and regulatory compliance.

Comments and Discussions

 
GeneralMy vote of 5 Pin
MarkBoreham20-May-14 2:53
professionalMarkBoreham20-May-14 2:53 
GeneralMy vote of 4 Pin
Sean J Seymour26-Aug-13 3:43
Sean J Seymour26-Aug-13 3:43 
GeneralRe: My vote of 4 Pin
Crooked Path26-Aug-13 4:03
Crooked Path26-Aug-13 4:03 
QuestionWhat's the diffrence between MMF and MemoryStream? Pin
moein.serpico24-Oct-12 2:45
moein.serpico24-Oct-12 2:45 
AnswerRe: What's the diffrence between MMF and MemoryStream? Pin
Crooked Path24-Oct-12 4:39
Crooked Path24-Oct-12 4:39 
GeneralRe: What's the diffrence between MMF and MemoryStream? Pin
moein.serpico24-Oct-12 9:16
moein.serpico24-Oct-12 9:16 
QuestionWhere is the code for this articel? Pin
Member 850471223-Oct-12 6:19
Member 850471223-Oct-12 6:19 
AnswerRe: Where is the code for this articel? Pin
Crooked Path23-Oct-12 7:57
Crooked Path23-Oct-12 7:57 
GeneralRe: Where is the code for this articel? Pin
Crooked Path24-Oct-12 7:50
Crooked Path24-Oct-12 7:50 
Problem resolved.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.