首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在将结构数组从c编组到c#时遇到问题

在将结构数组从c编组到c#时遇到问题
EN

Stack Overflow用户
提问于 2020-12-31 22:28:14
回答 1查看 148关注 0票数 0

我试图将一个c结构数组编组到C#中(使用Unity),但是,无论我使用哪种方法,我总是得到一个异常或崩溃。

我正在加载符合(或者应该...)的dll (libretro核心)。对于Libretro API,c/c++端对我来说是不可用的(更准确地说,我不允许被我修改),这意味着我必须处理从该dll返回的数据,无论它是如何布局的。

C应用编程接口结构的定义如下(RETRO_NUM_CORE_OPTION_VALUES_MAX是一个值为128的常量):

代码语言:javascript
复制
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#映射如下所示:

代码语言:javascript
复制
[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#端有如下签名:

代码语言:javascript
复制
public unsafe bool Callback(retro_environment cmd, void* data)

retro_environment是一个转换为枚举的无符号整数,对它执行一个开关,然后指示如何适当地处理空*数据指针。这里的数据是一个retro_core_options_intl*。

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

代码语言:javascript
复制
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);

代码语言:javascript
复制
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个值。

我试过了:

代码语言:javascript
复制
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。

代码语言:javascript
复制
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数组。

代码语言:javascript
复制
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

EN

回答 1

Stack Overflow用户

发布于 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字符串。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65522114

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档