使用字节数组从内存映射文件获取动态类
本文关键字:文件 映射 获取 动态 内存 字节 字节数 数组 | 更新日期: 2023-09-27 18:16:47
我以为我已经完成了这个项目,但在终点线我遇到了一个大问题…
这是我的类库(MemoryMapTool.cs)
using System;
using System.IO.MemoryMappedFiles;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Runtime.Serialization;
using System.Diagnostics;
namespace SharedMemoryWorker
{
public class MemoryMapTool<T> : IDisposable where T: class
{
#region Private class variables
private string m_sLastError = "";
private MemoryMappedFile mmf = null;
private MemoryMappedViewAccessor accessor = null;
private string m_sMapName = "";
private T m_oClassObject = null;
private byte[] memoryBytes = null;
private int iMapSize = 0;
private bool bLocked = false;
private Mutex mxLock = null;
#endregion
#region Public properties
public string MapName
{
get
{
return m_sMapName;
}
set
{
m_sMapName = value;
}
}
public T ClassObject
{
get
{
//Read class object from memory
accessor.ReadArray<byte>(0, memoryBytes, 0, iMapSize);
//Convert the byte array to a class object
m_oClassObject = ConvertByteArrayToObject(memoryBytes);
return m_oClassObject;
}
set
{
//Write class object to memory
m_oClassObject = value;
//Lock the mutex
mxLock.WaitOne();
//Convert the class object to a byte array
memoryBytes = ConvertObjectToByteArray(m_oClassObject);
//Write the byte array to memory
accessor.WriteArray<byte>(0, memoryBytes, 0, iMapSize);
//Unlock the mutex
mxLock.ReleaseMutex();
}
}
#endregion
#region Constructor
public MemoryMapTool(string sMapName, T oClassObject)
{
try
{
//Save the object
m_oClassObject = oClassObject;
//Save the map name
m_sMapName = sMapName.ToLower();
//Convert the class or struct object to a byte array
memoryBytes = ConvertObjectToByteArray(m_oClassObject);
iMapSize = memoryBytes.Length;
Debug.WriteLine("MapSize:" + iMapSize.ToString());
}
catch (Exception ex)
{
m_sLastError = ex.Message;
throw new NullReferenceException("Error creating new object!");
}
}
public void Dispose()
{
//Deconstructor
CloseMemoryMap();
}
#endregion
#region Public class methods
public string GetLastError()
{
return m_sLastError;
}
public bool OpenMemoryMap()
{
try
{
//Create new map or use an existing one
mmf = MemoryMappedFile.CreateOrOpen(m_sMapName, iMapSize);
//Lock the mutex
mxLock = new Mutex(true, m_sMapName + "_ipc", out bLocked);
//Create the memory map view accessor
accessor = mmf.CreateViewAccessor(0, iMapSize, MemoryMappedFileAccess.ReadWrite);
//Unlock mutex
mxLock.ReleaseMutex();
}
catch
{
//Return error
return false;
}
//Return success
return true;
}
public void CloseMemoryMap()
{
//Destory the memory map view accessor in needed
if (accessor != null) accessor.Dispose();
//Destory the memory map file if needed
if (mmf != null) mmf.Dispose();
//Get rid of the mutex lock if needed
if (mxLock != null) mxLock.Close();
bLocked = false;
}
#endregion
#region Private class methods
private byte[] ConvertObjectToByteArray(T sourceObj)
{
byte[] byteArray = null;
IFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
formatter.Serialize(stream, sourceObj);
byteArray = stream.ToArray();
}
return byteArray;
}
private T ConvertByteArrayToObject(byte[] sourceBytes)
{
object newObject = null;
IFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream(sourceBytes))
{
newObject = formatter.Deserialize(stream);
}
return (T)newObject;
}
#endregion
}
}
这是我的测试应用程序"MyAppA" (form .cs)
using System;
using System.Windows.Forms;
using SharedMemoryWorker;
namespace MyAppA
{
public partial class Form1 : Form
{
#region Public classes
[Serializable()]
public class MySClass
{
public string Test = "Test 1";
public string Test2 = "Test 2";
public string Test3 = "Test 3";
public int Test4 = 4;
}
#endregion
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MySClass myClass = new MySClass();
myClass.Test = "This is my test string";
using (MemoryMapTool<MySClass> mmt = new MemoryMapTool<MySClass>("testmap", myClass))
{
//do stuff
}
}
}
}
所以这个问题是一个简单的问题,但我不确定解决方案有多简单(如果存在的话)…目前我可以使用我在myapp . form1。button1_Click获取我在Form1上的类对象,并从类对象(MySClass - AKA myClass)创建字节数组(使用序列化)。这很好,我得到了…
Debug.WriteLine("MapSize:" + iMapSize.ToString());
…
MapSize: 204
完美!知道大小为204,我还可以通过提取204字节并再次将它们转换为类对象来检索内存映射。简单!
但是等等,有一个问题…如果我改变myClass。测试字符串为…
myClass.Test = "This is my test string2";
好crud . .现在尺寸是205!!我想回想起来,我也有这样的期望。那么,当我有另一个应用程序,说MyAppB使用相同的代码,并试图从内存拉类对象发生了什么?的默认大小(序列化)
[Serializable()]
public class MySClass
{
public string Test = "";
public string Test2 = "";
public string Test3 = "";
public int Test4 = 4;
}
…是MapSize: 168。我可以调整我的类来计算对象的大小,当我写没有问题,但你如何让它在阅读时工作?
可以序列化为JSON或XML,而不是序列化为二进制形式。当然,这会增加序列化对象的大小,但处理起来要容易得多。有几种方法可以做到这一点。下面是我在这个网站上找到的一个例子:
var thing = new Thing();
var json = new JavaScriptSerializer().Serialize(thing);
在以这样的标准方式序列化之前,您的类可能需要进行一些调整。
来源:c#中的Json序列化
就像之前别人说的…你的问题与内存映射数据的长度有关,所以当你写文件时,一定要先写字节数组的长度。
MMF =>
读取文件时,首先读取长度描述符,然后初始化所读取长度的数组,并用来自mmf的数据填充它。