使用 .NET 对象进行内存映射文件互操作






4.76/5 (11投票s)
本文提供了读/写 .NET 对象,目标是 Windows 内存映射文件 API 的基本框架。
引言
Microsoft 扩展了 .NET 4.0 API,包含了内存映射文件 (MMF),显著提升了 .NET 与其他 Windows 架构的互操作性。 Microsoft 和其他地方提供了很好的文章和 C# 示例,演示了进程间通信的二进制(字节数组)I/O,但几乎没有关于实际使用的信息,例如读/写简单数据类型以外的任何内容。 读/写二进制 .NET 托管对象是挑战。
本文提供了使用 System.IO.MemoryMappedFiles
命名空间中的 .NET API 读/写复杂 .NET 对象变量的基本框架。
使用代码
此代码演示了简单的 MMF I/O,包括 MMF 字节数组和托管 .NET 对象之间的二进制序列化。
那么为什么不使用 XML 序列化器并写入 ASCII? 凡事都有时间和地点。
首先,以下示例假设
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.Serialization.Formatters.Binary;
写入到 MMF
让我们从一个 .NET 托管类开始,它用作数据容器。 应该序列化到 MMF 的所有内容都必须标记为 Serializable。 在这种情况下,这是包含 GPS 轨迹的简单徒步旅行数据库的结构。
[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;
}
接下来,假设已经收集了数据。 我们现在需要一种简单的方法将托管对象作为二进制文件写入 MMF,以便与此内存映射文件的其他用户共享:
// Write managed object to MMF
WriteObjectToMMF("C:\\TEMP\\TEST.MMF", hikingData);
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);
}
}
}
大多数 MMF 示例中缺少的步骤是将托管对象转换为其二进制形式的“ObjectToByteArray
”函数。
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
}
当然,在将代码投入生产之前,您需要用 try/catch 包装所有这些东西。
从 MMF 读取
您现在有一个文件,将托管对象持久化以供其他进程使用。 这些其他进程将需要读取这些相同的对象。 非常简单,这是通过打开 MMF,将字节数组读入缓冲区,并将该缓冲区转换回托管对象来完成的:
// Read managed object from MMF
HikingDatabase hikingData = ReadObjectFromMMF("C:\\TEMP\\TEST.MMF") as HikingDatabase;
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);
}
}
}
同样,下面是配套的二进制到托管 ByteArrayToObject
函数。
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
}
如前所述,您需要用 try/catch
包装这些代码。
兴趣点
我特意使用了 API,避免了 MMF 中的偏移量,因为它们支持对多个内存缓冲区的随机访问。 虽然随机访问对于持久化多个对象很有用,但对于整个文件的 I/O 来说,它的性能不太好。 在读/写整个文件时,我建议使用 CreateViewStream
代替 CreateViewAccessor
。
当多个进程共享一个打开的文件时,最好创建一个命名的互斥锁来管理并发。 不这样做会导致不希望的行为,并且很难调试。
有关更多信息,我建议从 CodeProject 和 MSDN 的以下文章开始:
- CodeProject:使用 .NET Framework 进行内存映射文件编程
- MSDN Utopia 博客:在 .NET 4.0 中使用内存映射文件。
- MSDN:内存映射文件(参见“非 {Persisted Memory-Mapped files for details using a Mutex)”的详细信息,使用互斥锁)
还有很多很多其他的好文章。 您只需要搜索。
历史
- 2012-10-02 - 初始发布。