1

C# アプリケーションで C で記述された DLL を使用しようとしています。私が抱えている問題を再現する簡単な例を作成しました。

以下の C コードは、 の配列を作成し、関数に渡されstruct dataたパラメーターに配列ポインターを割り当てます。C# コードは、C# で使用される構造体をマーシャリングするために必要な定型コードであると想定されていますが、問題が発生しています。arrayget_data()

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 関数で作成された配列を正しく読み取るための助けを得ることができますか?

編集(David Heffernanのコメントによる)

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