【问题标题】:Having trouble marshalling an array of structs from c to c#无法将结构数组从 c 编组到 c#
【发布时间】:2020-12-31 14:28:14
【问题描述】:

我正在尝试将一组 c 结构编组到 C#(使用 Unity)中,但是,无论我使用哪种方法,我总是会遇到异常或崩溃。

我正在加载符合(或应该...)Libretro API 的 dll(libretro 核心),我无法使用 c/c++ 端(更准确地说,不允许我修改),这意味着我必须处理从该 dll 中获取的数据,无论其布局如何。

C API 结构定义如下(RETRO_NUM_CORE_OPTION_VALUES_MAX 是一个值为 128 的常量):

struct retro_core_option_value
{
   const char *value;
   const char *label;
};

struct retro_core_option_definition
{
   const char *key;
   const char *desc;
   const char *info;
   struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX];
   const char *default_value;
};

struct retro_core_options_intl
{
   struct retro_core_option_definition *us;
   struct retro_core_option_definition *local;
};

目前我的 C# 映射如下所示:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct retro_core_option_value
{
    public char* value;
    public char* label;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct retro_core_option_definition
{
    public char* key;
    public char* desc;
    public char* info;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = RETRO_NUM_CORE_OPTION_VALUES_MAX)]
    public retro_core_option_value[] values;
    public char* default_value;
}

[StructLayout(LayoutKind.Sequential)]
public struct retro_core_options_intl
{
    public IntPtr us;
    public IntPtr local;
}

回调函数在 C# 端具有以下签名:

public unsafe bool Callback(retro_environment cmd, void* data)

retro_environment 是一个转换为枚举的无符号整数,对其执行切换,然后指示如何正确处理 void* 数据指针。这里的数据是一个 retro_core_options_intl*。

我可以通过 2 种方式进行 void* 转换:

retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);

retro_core_options_intl* intl = (retro_core_options_intl*)data;

我用这两种方法都得到了一个可读的地址(第一种方法是 intl.us,第二种方法是 intl->us),在我的特殊情况下,“本地”部分是空的,但“我们”部分被定义为强制性的API。 intl->us 指向一个可变长度的 retro_core_option_definition 数组。

我遇到的问题是尝试读取此强制构造中的值。

我现在尝试加载的数组可以在这里看到:https://github.com/visualboyadvance-m/visualboyadvance-m/blob/master/src/libretro/libretro_core_options.h at line 51。

API 为“struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX]”结构成员定义了一个固定大小,但进来的代码几乎总是定义为一个数组,其中最后一个元素是“{ NULL, NULL }”以表示结束,因此它们并不总是(几乎从不)包含 128 个值。

我试过了:

retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition us = Marshal.PtrToStructure<retro_core_option_definition>(intl.us);

这会产生 NullReferenceException。

retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition[] us = Marshal.PtrToStructure<retro_core_option_definition[]>(intl.us);

这给出了一个长度为 0 的 retro_core_option_definition 数组。

retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition us = new retro_core_option_definition();
Marshal.PtrToStructure(intl.us, us);

这给出了一个“目的地是一个装箱的值”。

这基本上就是我所在的地方...... 任何帮助将不胜感激:)

整个代码库可以在这里找到:https://github.com/Skurdt/LibretroUnityFE

【问题讨论】:

  • 不知道这是否有帮助,但我偶然发现了这个,它谈到了 c 和 c# 与 char 类型的区别:stackoverflow.com/questions/11511333/…
  • 哦,兄弟。在 Unity 插件中编组是如此困难,我会放弃回家 :O 这是适用于 iOS 还是什么平台?
  • 该程序本身已经能够在 windows、linux、macos、android 和 ios 上运行基础知识(也就是加载一些内核并正确启动一些游戏)。一些更麻烦的功能只在 windows 上运行,一个只在 windows 上运行,同时强制 opengl 后端。虽然它并不完美,但大多数问题已经得到了一定程度的解决。但是这个问题真的让我眼前一亮,我尝试了很多解决方案......我开始将数据发送到 c++ 并在那里读取它(这似乎有效)但如果它有的话我宁愿有一个 c# 解决方案首先可能。

标签: c# unity3d emulation libretro


【解决方案1】:

我看到的第一件事是,您要么需要在 C 代码中使用 wchar_t 类型而不是 char 类型,要么在 C# 中使用 byte 而不是 char。 C# 中的System.Char 是两个字节。 C 代码中的char 是 1 个字节。

您还可以在 C# 代码中使用 System.String 并使用 MarshalAs 属性对其进行注释,以告诉它输入的是什么类型的 char 数据,例如 Ansi 或 Unicode C 字符串。

【讨论】:

  • 我什至不知道MarshalAs,谢谢!
  • 将所有 char* 更改为 string 具有相同的结果。我有一个 .cs 文件,它模仿 .h C 标头用于编组目的,到目前为止,几乎所有这些文件都或多或少地按应有的方式工作。一些使用字符串类型工作,而另一些则需要使用 char* 然后使用 Marshal.PtrToStringAnsi 进行转换(我在这里松散地使用“需要”这个词,结果总是/总是不可预测的,因为不同提供者之间的外部 dll 差异很大....这是应该与我的项目兼容的所有内核的列表:buildbot.libretro.com/nightly/windows/x86_64/latest)
猜你喜欢
  • 2010-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-01
相关资源
最近更新 更多