问题简而言之:
如何获取作为对象传递给类库的用户定义结构的大小?
概述:
此测试项目的重点是构建一个类库,该类库使用 .Net 4(或更少)中的内存映射来包装内存共享。最后,我希望能够在我的主应用程序中定义一个结构,将其传递给类库包装器并让类库确定结构的大小。
定义:
- MyAppA:主应用程序,将创建内存映射的应用程序 实例最初。
- MyAppB:用于通信的第二个应用程序 使用 MyAppA。这将利用现有的内存映射。
- MemoryMapTool:这将是封装所有内存共享的类库。
- 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)中坚持使用。
最后,我想说的是,这并不真正适合您要尝试执行的操作(太多的开销和出错的空间)。相反,您可以:
- 仍然使用 MMF,但使用 binary serialization 序列化您的类甚至 JSON。
- 设计基于 WCF 或 WebAPI 的面向服务的架构(现在可以通过 OWIN/Katana 自托管)。
- 最终您还可以使用原始 TCP/IP 套接字并为您的应用设计一个小型协议(protocol)。
我的选择是#2。性能可以非常好,尤其是使用 WCF 和在具有命名管道或 net.tcp 绑定(bind)的同一台计算机上,它就可以正常工作。
关于c# - 获取用于内存映射的结构的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25087687/