【发布时间】: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