1

我正在尝试在 C# 应用程序中使用用 C 编写的 DLL。我创建了一个简化的示例来复制我遇到的问题。

下面的 C 代码创建一个struct data's 数组并将数组指针分配给array传递给get_data()函数的参数。C# 代码应该是编组要在 C# 中使用的结构所需的样板代码,但它给我带来了问题。

C代码

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

struct data {
    int32_t value;
    union {
        struct person {
            uint8_t first[10];
            uint8_t last[10];
        } person;
        struct number {
            int32_t imaginary;
            int32_t real;
        } number;
    } type;
};

int get_data(int count, struct data ***array)
{
    int i;

    /* allocate pointers */
    *array = calloc(count, sizeof(struct data*));
    if (*array == NULL)
        return 1;

    for (i = 0; i < count; i++) {
        /* allocate data struct */
        struct data *data = calloc(1, sizeof(struct data));
        if (data == NULL)
            return 2;

        if ((i % 2) == 0) {
            /* if even, its human */
            data->value = i;
            memcpy(data->type.person.first, "john", 4);
            memcpy(data->type.person.last, "doe", 3);
        } else {
            /* if odd its a number */
            data->value = i;
            data->type.number.imaginary = -1;
            data->type.number.real = i + 1;
        }

        (*array)[i] = data;
    }

    return 0;
}

C# 代码

[DllImport("libdata.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 get_data(Int32 count, ref IntPtr array);

[StructLayout(LayoutKind.Sequential)]
public struct Person
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public String first;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public String last;
}

[StructLayout(LayoutKind.Sequential)]
public struct Number
{
    public Int32 imaginary;
    public Int32 real;
}

[StructLayout(LayoutKind.Explicit)]
public struct TypeUnion
{
    [FieldOffset(0)]
    public Person person;
    [FieldOffset(0)]
    public Number number;
}

[StructLayout(LayoutKind.Sequential)]
public struct Data
{
    public Int32 value;
    public TypeUnion type;
}

现在,当我运行我的测试程序时,我得到了一个异常:

System.TypeLoadException was unhandled
  Message=Could not load type 'WpfRibbonApplication1.TypeUnion' from assembly 'WpfRibbonApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.

我尝试了几种不同的编组Person字符串的方法,但无论我尝试哪种方式都会得到异常(使用作为参考)。我错过了一些明显的东西吗?我能否获得一些帮助来正确读取在我的 C# 应用程序中的 C 函数中创建的数组?

编辑(根据大卫赫弗南的评论)

IntPtr arrayPtr = new IntPtr();
int count = 4;
int ret = LibData.get_data(count, ref arrayPtr);
Console.WriteLine("ret=" + ret);

for (int i = 0; i < count; i++)
{
    IntPtr dataPtr = (IntPtr)Marshal.ReadIntPtr(arrayPtr) + (i * Marshal.SizeOf(typeof(IntPtr)));
    Data data = (Data)Marshal.PtrToStructure(dataPtr, typeof(Data));
    Console.WriteLine("value=" + data.value);
    if ((i % 2) == 0)
    {
        // even is human
        Console.WriteLine("first=" + data.type.first);
        Console.WriteLine("last=" + data.type.last);
    }
    else
    {
        // odd is number
        Console.WriteLine("imaginary=" + data.type.imaginary);
        Console.WriteLine("real=" + data.type.real);
    }
    Console.WriteLine("");
}
4

1 回答 1

1

错误消息告诉您不能用非对象字段覆盖对象字段。您正在string用a 覆盖 a int

使用FieldOffset复制本地联合是无法解决的。在我看来,您有两个主要选择:

  1. 停止使用联合并在结构中包含Person和结构。NumberData
  2. 继续使用联合,但自己编组。使用Marshal类读取结构中的数据。例如,您将使用Marshal.ReadInt32读取整数、Marshal.Copy读取字符数组等。
于 2013-10-28T16:40:10.990 回答