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

我在C#(ST_Layer)中创建了一个 struct ,它包含另一个 struct (ST_Point)的项array.为了访问这些项目,我定义了一个函数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或"Any CPU"("System. System.AdditionViolationException"exception in "layer. getPoint(0)—X")则不行.>这是一种预期的行为吗?如何解决此异常?
  • 我使用getPoint函数来访问ST_Point数组,因为我还没有看到更好的访问方法.是否可以将其作为普通数组(即layer.Points[0].X)进行访问?我不能创建ST_Point(layer.Points = new ST_Point[10])的数组,因为它将在ST_Layer之外创建,并且数据不会传递给Python.

我见过thisthis,但不知道如何访问ST_Point的各个字段.

感谢你的帮助.

[Also谢谢R.Martinho Fernandes为你的hint regarding the x86/x64 issue]

推荐答案

如果您使用的是最新版本的.NET,则可以使用System.Runtime.CompilerServices.InlineArray直接从MMF读取 struct 和将 struct 写入MMF,而不需要任何不安全的代码.

InlineArray属性允许你指定一个 struct ,它包含固定数量的特定值类型的元素,可以通过索引操作访问:

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($"{t.Points[i].X}, {t.Points[i].Y}");
        }
    }
}

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

// This is the crucial bit: Define a fixed inline array with 10 elements:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct TenPoints
{
    ST_Point _firstElement;
}

[StructLayout(LayoutKind.Explicit)]
{
    [FieldOffset(0)] public ushort nPoints;
    [FieldOffset(8)] public double fHeight;
    [FieldOffset(16)] public TenPoints Points;
}

Csharp相关问答推荐

当我使用NET6作为目标框架时,为什么DotNet使用NET8作为MS包?

实现List T,为什么LINQ之后它不会返回MyList?<>(无法强制转换WhereListIterator `1类型的对象)'

REST API端点中异步后台代码执行的类型

无法通过绑定禁用条目

使用预定义对象减少Task.Run/Factory.StartNew中的关闭开销

调用Task.Run()与DoSomethingAsync()有什么不同?

如何在NET 8最小API中自动记录TypedResults.Stream响应

使用带有参数和曲面的注入失败(&Q;)

如何在Cosmos SDK中控制超时、重试和重试之间的延迟?

是否有必要在ASP.NET Core中注册可传递依赖项?

在被Interactive Server切换后,Blazor SSR页面无法正确加载JS

将操作从编辑页重定向到带参数的索引页

在集成测试中可以在模拟InMemory数据库中设定数据种子

在';、';附近有错误的语法.必须声明标量变量";@Checkin";.';

KeyDown从我的文本框中删除输入,如何停止?

WPF如何获取有关从一个视图模型更改另一个视图模型的信息

从MudAutoComplete打开对话框,列表仍然可见

在使用.NET EF Core DbContext属性之前,是否应使用null判断

如何将行添加到DataGrid以立即显示它?

使用Try-Catch-Finally为API端点处理代码--有什么缺点?