Mik*_*ved 7 c# struct marshalling memory-mapped-files
I have a struct that is defined with Pack=1 and it is 29 bytes long. If it is not packed, it would be 32 bytes long.
Marshal.SizeOf(TypeOf(StructName)) returns 29.
StructName struct; sizeof(struct) returns 32.
When I write that struct out using MemoryMappedViewAccessor it writes out 32 bytes, NOT 29 bytes.
So, short of marshalling the struct to a byte array and writing it out that way, is there any way to get it to write out that struct correctly?
More detail: if you use Explicit layout, Write will, in fact, write out 29 bytes. WriteArray, though, writes out 32 bytes for each element.
而avip,是的,细致的字节序列化可能会起作用,但是(我没有对它进行分析,但我猜)它可能比WriteArray慢几个数量级,不是吗?
编辑:好的,我终于明白了你真正的要求.我们通常不会MemoryMappedViewAccessor用来序列化对象,现在你知道为什么了.
以下内容将为您提供预期的结果.
public static class ByteSerializer
{
public static Byte[] Serialize<T>(IEnumerable<T> msg) where T : struct
{
List<byte> res = new List<byte>();
foreach (var s in msg)
{
res.AddRange(Serialize(s));
}
return res.ToArray();
}
public static Byte[] Serialize<T>(T msg) where T : struct
{
int objsize = Marshal.SizeOf(typeof(T));
Byte[] ret = new Byte[objsize];
IntPtr buff = Marshal.AllocHGlobal(objsize);
Marshal.StructureToPtr(msg, buff, true);
Marshal.Copy(buff, ret, 0, objsize);
Marshal.FreeHGlobal(buff);
return ret;
}
}
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Yours
{
public Int64 int1;
public DateTime dt1;
public float f1;
public float f2;
public float f3;
public byte b;
}
static void Main()
{
var file = @"c:\temp\test.bin";
IEnumerable<Yours> t = new Yours[3];
File.WriteAllBytes(file, ByteSerializer.Serialize(t));
using (var stream = File.OpenRead(file))
{
Console.WriteLine("file size: " + stream.Length);
}
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:所以似乎DateTime真的喜欢在对齐的内存地址上.虽然您可以定义显式布局,但我认为更简单的方法是:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Test
{
private long dt1;
public byte b;
public Int64 int1;
public float f1;
public float f2;
public float f3;
public DateTime DT
{
get { return new DateTime(dt1); }
set { dt1 = value.Ticks; }
}
}
Run Code Online (Sandbox Code Playgroud)
虽然我不明白为什么你应该关心托管内存表示.
或者,[StructLayout(LayoutKind.Explicit)]应防止内存对齐.
示例('托管sizeof'取自此帖子)
[StructLayout(LayoutKind.Explicit, Size = 9)]
public struct Test
{
[FieldOffset(0)]
public DateTime dt1;
[FieldOffset(8)]
public byte b;
}
class Program
{
static readonly Func<Type, uint> SizeOfType = (Func<Type, uint>)Delegate.CreateDelegate(typeof(Func<Type, uint>), typeof(Marshal).GetMethod("SizeOfType", BindingFlags.NonPublic | BindingFlags.Static));
static void Main()
{
Test t = new Test() { dt1 = DateTime.MaxValue, b = 42 };
Console.WriteLine("Managed size: " + SizeOfType(typeof(Test)));
Console.WriteLine("Unmanaged size: " + Marshal.SizeOf(t));
using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 1))
using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
{
accessor.Write(0L, ref t);
long pos = 0;
for (int i = 0; i < 9; i++)
Console.Write("|" + accessor.ReadByte(pos++));
Console.Write("|\n");
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
Managed size: 9
Unmanaged size: 9
|255|63|55|244|117|40|202|43|42| // managed memory layout is as expected
Run Code Online (Sandbox Code Playgroud)
BTW,DateTime似乎打破了顺序合同 - 但请记住合同是用于Marshaled内存映射.关于托管内存布局没有规范.
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 9)]
public struct Test
{
public DateTime dt1;
public byte b;
}
Run Code Online (Sandbox Code Playgroud)
以上代码的输出:
Managed size: 12
Unmanaged size: 9
|42|0|0|0|255|63|55|244|117|40|202|43| // finally found those 3 missing bytes :-)
Run Code Online (Sandbox Code Playgroud)