我试图将一个c结构数组编组到C#中(使用Unity),但是,无论我使用哪种方法,我总是得到一个异常或崩溃。
我正在加载符合(或者应该...)的dll (libretro核心)。对于Libretro API,c/c++端对我来说是不可用的(更准确地说,我不允许被我修改),这意味着我必须处理从该dll返回的数据,无论它是如何布局的。
C应用编程接口结构的定义如下(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是一个转换为枚举的无符号整数,对它执行一个开关,然后指示如何适当地处理空*数据指针。这里的数据是一个retro_core_options_intl*。
我可以通过两种方式进行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 ),在我的特殊情况下,“本地”部分是空的,但"us“部分被API定义为强制的。intl->us指向一个可变长度retro_core_option_definition数组。
我遇到的问题是试图读取这个强制构造中的值。
我现在尝试加载的数组可以在这里看到: 51行的https://github.com/visualboyadvance-m/visualboyadvance-m/blob/master/src/libretro/libretro_core_options.h。
API为"struct NULL NULL“结构成员定义了一个固定的大小,但是传入的代码几乎总是被定义为一个数组,其中最后一个元素是"{ retro_core_option_value,valuesRETRO_NUM_CORE_OPTION_VALUES_MAX }”以表示结束,因此它们并不总是(几乎从不)包含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);这给出了一个"destination is a boxed value“。
这基本上就是我所在的..。如有任何帮助,我们将不胜感激:)
整个代码库可以在这里找到:https://github.com/Skurdt/LibretroUnityFE
发布于 2020-12-31 23:25:22
我首先看到的是,您要么需要在C代码中使用wchar_t类型而不是char类型,要么在C#中使用byte而不是char类型。C#中的System.Char是两个字节。C代码中的char是1个字节。
您还可以在C#代码中使用System.String,并使用MarshalAs属性对其进行注释,以告诉它传入的字符数据类型,例如Ansi或Unicode C字符串。
https://stackoverflow.com/questions/65522114
复制相似问题