c# - 获取用于内存映射的结构的大小

标签 c# memory structure shared-memory

问题简而言之:

如何获取作为对象传递给类库的用户定义结构的大小?

概述:

此测试项目的重点是构建一个类库,该类库使用 .Net 4(或更少)中的内存映射来包装内存共享。最后,我希望能够在我的主应用程序中定义一个结构,将其传递给类库包装器并让类库确定结构的大小。

定义:

  1. MyAppA:主应用程序,将创建内存映射的应用程序 实例最初。
  2. MyAppB:用于通信的第二个应用程序 使用 MyAppA。这将利用现有的内存映射。
  3. MemoryMapTool:这将是封装所有内存共享的类库。
  4. TestStruct:这将是 MyAppA 和 MyAppB 中定义的结构,在两个应用程序中完全相同,但可能会不时更改。 MemoryMapTool 在任何时候都不会知道结构布局,它只会将其视为一个对象。

初步想法:

我希望类库包装器不知道 MyAppA 生成的 TestStruct,除了它是类库包装器需要跟上并用于内存共享的对象...

我想我会在 MyAppA 中创建 TestStruct 并根据需要向其中添加尽可能多的变量(在本例中只有 1,一个字符串)。然后将其传递给 MemoryMapTool 构造函数,让 MemoryMapTool 类确定结构的大小。这是目前的问题。在使用内存时,我倾向于谨慎并进行研究,然后再尝试可能无法杀死我的 IDE 或操作系统的东西……;)

我原本打算直接将 TestStruct 传递给 MemoryMapTool 构造函数,但遇到了这个问题......

long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(oData));

错误:找不到类型或命名空间名称“oData”(是否缺少 using 指令或程序集引用?)

然后我想尝试只使用...

long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(oData);

... 它似乎可以工作(至少 IDE 喜欢它)。但出于某种原因,我觉得这不是正确的做法。

更新:在尝试之后我得到了一个新的错误...

错误:无法将“MyAppA.Form1+TestStruct”类型编码为非托管结构;无法计算有意义的大小或偏移量。


当前资源

MemoryMapTool.cs 内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.MemoryMappedFiles;
using System.Threading;

namespace SharedMemoryWorker
{
    public class MemoryMapTool : IDisposable
    {
        #region Private class variables
        private string m_sLastError = "";
        private MemoryMappedFile mmf = null;
        private string m_sMapName = "";
        private object m_oData = null;
        #endregion

        #region Public properties
        public string MapName
        {
            get
            {
                return m_sMapName;
            }
            set
            {
                m_sMapName = value;
            }
        }

        public object Data
        {
            get
            {
                return m_oData;
            }
            set
            {
                m_oData = value;
            }
        }
        #endregion

        #region Constructor
        private MemoryMapTool(string sMapName, object oData)
        {
            long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(oData);

            try
            {
                //Save the map name
                m_sMapName = sMapName;

                //Create new map or use an existing one
                //mmf = MemoryMappedFile.CreateOrOpen(m_sMapName, lMapSize);


            }
            catch (Exception ex)
            {
                m_sLastError = ex.Message;
                throw new NullReferenceException("Error creating new object!");
            }
        }

        public void Dispose()
        {
            //Deconstructor
        }
        #endregion

        #region Public class methods
        public string GetLastError()
        {
            return m_sLastError;
        }
        #endregion

    }
}

MyAppA、Form1.cs 内容

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MyAppA
{
    public partial class Form1 : Form
    {
        #region Public structures
        public class TestStruct
        {
            #region Private class variables
            private string m_sTest = null;
            #endregion

            #region Public properties
            public string Test
            {
                get
                {
                    return m_sTest;
                }
                set
                {
                    m_sTest = value;
                }
            }
            #endregion
        }
        #endregion

        public Form1()
        {
            InitializeComponent();
        }
    }
}

MyAppB、Form1.cs 内容

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MyAppB
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    }
}

最佳答案

我认为错误很明显。您需要告诉 CLR 您希望结构或类成员如何在内存中布局。查看StructLayoutAttribute - 它需要显式应用于类(而结构 Sequential 是默认值)。

根据您的描述,您似乎想在两个或多个托管进程之间执行 IPC。您可能还想为字符串建立一个统一的编码策略(请参阅 MarshalAsAttribute)。您可以选择一个并在整个类(class)中坚持使用。

最后,我想说的是,这并不真正适合您要尝试执行的操作(太多的开销和出错的空间)。相反,您可以:

  1. 仍然使用 MMF,但使用 binary serialization 序列化您的类甚至 JSON。
  2. 设计基于 WCF 或 WebAPI 的面向服务的架构(现在可以通过 OWIN/Katana 自托管)。
  3. 最终您还可以使用原始 TCP/IP 套接字并为您的应用设计一个小型协议(protocol)。

我的选择是#2。性能可以非常好,尤其是使用 WCF 和在具有命名管道或 net.tcp 绑定(bind)的同一台计算机上,它就可以正常工作。

关于c# - 获取用于内存映射的结构的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25087687/

相关文章:

c# - 看似无辜的 .NET 正则表达式使页面响应时间增加 10 倍

python - 重建索引时 Pandas 的内存使用情况

string - 为什么使用 const 字符串参数时程序会崩溃?

c - 尝试填充结构中包含的矩阵时出现段错误

c - 错误 *** 没有名为 *** 的成员

go - 如何创建一个函数来接受来自任何结构的任何对象的任何调用?

c# - 我应该使用 Guid 和 Guid.Empty 还是可以为 null 的 Guid?

c# - 如果过去 Y 秒内有 X 个元素,则弹出?

c# - 传递到字典中的模型项的类型为 'System.Data.Entity.DynamicProxies.People' ,但该字典需要类型为 的模型项

c - 为什么在 C 输入中需要变量的内存位置而不是变量本身的名称?