我正在尝试将一组 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第 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