【问题标题】:Getting the size of a structure meant for memory mapping获取用于内存映射的结构的大小
【发布时间】:2014-09-25 02:20:12
【问题描述】:

问题简述:

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

概述:

这个测试项目的重点是构建一个类库,该类库使用 .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();
        }
    }
}

【问题讨论】:

  • 你的TestStruct 根本不是一个结构,而是一个类。并且大小会根据您是作为 64 位还是 32 位应用程序运行而有所不同。
  • 是的,卡梅伦,我知道。在最初写下我的问题后,我做出了改变……我从一个结构开始,但由于某种原因无法工作的结构有问题,我不得不把它变成一个类。无法创建它的实例或类似的东西......如果需要,我可以将其设为结构。
  • @leppie 我不知道您为什么编辑我的问题以删除 c#4 标记...这就是我正在使用的框架,并想确保有人没有使用诸如 c 之类的解决方案#4.5+(4.5 内置了一些特殊的东西来帮助我实现我想要做的事情)。
  • @ArvoBowen:您没有特定于 C# 4 的代码。这一切都只是 C#。也许您不想使用 .NET 4.5,但我没有看到任何证据表明答案需要它。如果有,请在您的问题中告诉我们。通过再次阅读您的问题,使用 Remoting 或 WCF 很容易做到这一点。
  • 请注意,MemoryMappedFiles 是在 .NET 4.0 中首次引入 C#,因此您将无法定位任何较低版本。

标签: c# memory structure shared-memory


【解决方案1】:

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

从您的描述看来,您希望在两个或多个托管进程之间进行 IPC。您可能还想为字符串建立统一的编组策略(请参阅MarshalAsAttribute)。您可以选择一个并在整个课程中坚持使用它。

最后,我想说的是,这并不适合您正在尝试做的事情(开销太大且容易出错)。相反,您可以:

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

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

【讨论】:

  • 感谢您的意见!我应该使用类似 [StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)] 的东西吗?
  • 您可以使用Sequential,因为我怀疑您的目标是 C 联合行为。只需按照 MSDN 中的示例进行操作即可。
  • 我在想如果这是默认设置,它已经在这样做了。 ;) - 那么它会像做 [StructLayout(LayoutKind.Sequential)] 一样简单吗?
  • @ArvoBowen:这是结构的默认设置。你有一堂课。这真的取决于你以后用它做什么,所以我不能保证它会很容易。
  • 谢谢马塞尔!大有帮助!
猜你喜欢
  • 2023-03-03
  • 2021-06-04
  • 1970-01-01
  • 2010-11-22
  • 1970-01-01
  • 1970-01-01
  • 2022-11-02
  • 2014-04-23
  • 2011-10-11
相关资源
最近更新 更多