65.9K
CodeProject 正在变化。 阅读更多。
Home

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

2012 年 10 月 2 日

CPOL

2分钟阅读

viewsIcon

52399

downloadIcon

1395

本文提供了读/写 .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 的以下文章开始:

还有很多很多其他的好文章。 您只需要搜索。

历史 

  • 2012-10-02 - 初始发布。
© . All rights reserved.