使用 LayoutKind.Explicit 访问 C# 中的嵌套结构

问题描述 投票:0回答:1

我有一个 C# 应用程序,需要与 Python 共享一些二进制数据,并且我计划使用共享内存(内存映射文件)。因此,我需要两边都有相同的二进制结构。

我在 C# 中创建了一个结构体 (

ST_Layer
),其中包含另一个结构体 (
ST_Point
) 的项目数组。为了访问这些项目,我定义了一个函数
getPoint
,它将返回指向所请求的位置的指针
ST_Point

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

class TestProgram
{
    [StructLayout(LayoutKind.Explicit, Pack = 8)]
    public struct ST_Point
    {
        [FieldOffset(0)] public double X;
        [FieldOffset(8)] public double Y;
    }

    [StructLayout(LayoutKind.Explicit, Pack = 8)]
    public unsafe struct ST_Layer
    {
        [FieldOffset(0)] public ushort nPoints;
        [FieldOffset(8)] public double fHeight;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
        [FieldOffset(16)] public ST_Point[] Points;

        public unsafe ST_Point* getPoint(int n)
        {
            fixed (ST_Layer* thisPtr = &this)
            {
                int ptr = (int)&(thisPtr->Points) + n * ((16 + 3) / 4) * 4;   // Sizeof ST_Point 16
                return (ST_Point*)ptr;
            }
        }
    }

    static void Main(string[] args)
    {
        unsafe
        {
            ST_Layer layer = new ST_Layer();
            layer.nPoints = 3;
            layer.fHeight = 4;
            layer.getPoint(0)->X = 5;
            layer.getPoint(3)->Y = 6;

            for (int i = 0; i < 10; i++)
                Debug.WriteLine("Data [" + i + "] " + layer.getPoint(i)->X + ", " + layer.getPoint(i)->Y + " - ");
            while (true)
                Thread.Sleep(50);
        }
    }
}

我有两个问题:

  • 如果我为 x86 编译代码,则前面的代码有效,但不适用于 x64 或“任何 CPU”(“layer.getPoint(0)->X”中的“System.AccessViolationException”异常)。这是预期的行为吗?我该怎么做才能解决这个异常?
  • 我使用
    getPoint
    函数来访问
    ST_Point
    数组,因为我还没有看到更好的访问它的方法。是否可以将其作为普通数组访问(即
    layer.Points[0].X
    )?我无法创建
    ST_Point
    (
    layer.Points = new ST_Point[10]
    ) 数组,因为它将在
    ST_Layer
    之外创建,并且数据不会传递给 Python。

我见过 thisthis,但不知道如何访问

ST_Point
的各个字段。

谢谢您的帮助。

[另外,感谢 R. Martinho Fernandes 对于 x86/x64 问题的提示]

c# pointers struct pinvoke shared-memory
1个回答
0
投票

如果您使用的是最新版本的 .NET,您可以使用

System.Runtime.CompilerServices.InlineArray
直接从 MMF 读取和写入结构,无需任何不安全代码:

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

namespace Console1;

public static class Program
{
    static void Main()
    {
        using MemoryMappedFile mmf = MemoryMappedFile.CreateNew("MappedFile", 1000);

        var view = mmf.CreateViewAccessor();

        using UnmanagedMemoryAccessor accessor = view;

        var s = new ST_Layer();

        for (int i = 0; i < 10; ++i)
        {
            s.Points[i] = new ST_Point { X = i, Y = -i };
        }

        long offset = 500; // Arbitrary offset with the MMF.

        view.Write(offset, ref s);

        view.Read(offset, out ST_Layer t);

        for (int i = 0; i < 10; ++i)
        {
            Console.WriteLine($"{s.Points[i].X}, {s.Points[i].Y}");
        }
    }
}

[StructLayout(LayoutKind.Explicit, Pack = 8)]
public struct ST_Point
{
    [FieldOffset(0)] public double X;
    [FieldOffset(8)] public double Y;
}

[System.Runtime.CompilerServices.InlineArray(10)]
public struct TenPoints
{
    ST_Point _firstElement;
}

[StructLayout(LayoutKind.Explicit, Pack = 8)]
public struct ST_Layer
{
    [FieldOffset(0)] public ushort nPoints;
    [FieldOffset(8)] public double fHeight;
    [FieldOffset(16)] public TenPoints Points;
}
© www.soinside.com 2019 - 2024. All rights reserved.