使用字节数组从内存映射文件获取动态类

本文关键字:文件 映射 获取 动态 内存 字节 字节数 数组 | 更新日期: 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的数据填充它。