web-dev-qa-db-ja.com

C#へのC ++構造体配列のマーシャル

C++には次の構造体があります。

#define MAXCHARS 15

typedef struct 
{
    char data[MAXCHARS];
    int prob[MAXCHARS];
} LPRData;

そして、これらの構造の3つの配列を取得するために私がp /呼び出している関数:

void GetData(LPRData *data);

C++では、次のようなことをするだけです。

LPRData *Results;
Results = (LPRData *)malloc(MAXRESULTS*sizeof(LPRData));
GetData( Results );

そして、それはうまく動作しますが、C#では動作するように思えません。次のようなC#構造体を作成しました。

public struct LPRData
{

    /// char[15]
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string data;

    /// int[15]
    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
    public int[] prob;
}

そして、それらのうち3つの配列(およびそれらのすべてのサブ配列)を初期化し、これに渡すと:

GetData(LPRData[] data);

正常に戻りますが、LPRData配列のデータは変更されていません。

私は、3つのLPRDataのサイズの生のバイト配列を作成し、次のような関数プロトタイプに渡すことさえ試みました。

GetData(byte [] data);

しかし、その場合、最初のLPRData構造から「データ」文字列を取得しますが、同じLPRDataからの「prob」配列を含めて、その後は何も取得しません。

これを適切に処理する方法のアイデアはありますか?

28
Adam Haile

構造体のデクロレーションにいくつかの属性を追加してみます

[StructLayout(LayoutKind.Sequential, Size=TotalBytesInStruct),Serializable]
public struct LPRData
{
/// char[15]
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string data;

/// int[15]
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
public int[] prob;
}

*注意TotalBytesInStructは、変数を表すことを意図したものではありません

JaredParは、IntPtrクラスを使用すると役立つこともありますが、私はPInvokeを使用してからかなりの時間が経過しているため、さびています。

25
denny

ポインターを扱うときの1つのトリックは、IntPtrを使用することです。その後、ポインターでMarshal.PtrToStructureを使用し、構造のサイズに基づいて増分して結果を取得できます。

static extern void GetData([Out] out IntPtr ptr);

LPRData[] GetData()
{
    IntPtr value;
    LPRData[] array = new LPRData[3];
    GetData(out value);
    for (int i = 0; i < array.Length; i++)
    {
        array[i] = Marshal.PtrToStructure(value, typeof(LPRData));
        value += Marshal.SizeOf(typeof(LPRData));
    }
    return array;
}
13
JaredPar

PInvoke Interop Assistantが役立つ場合があります。 http://clrinterop.codeplex.com/releases/view/1412

3
GregUzelac

GetDataパラメーターを OutAttribute でマークしましたか?

InAttributeとOutAttributeの組み合わせは、配列およびフォーマットされた非blittable型に適用される場合に特に役立ちます。発信者は、両方の属性を適用した場合にのみ、着信者がこれらのタイプに加える変更を確認できます。

2
Constantin

同様のトピックが この質問 で議論され、結論の1つはCharSet名前付きパラメーターをCharSet.Ansiに設定する必要があるということでした。そうでなければ、char配列の代わりにwchar_t配列を作成します。したがって、正しいコードは次のようになります。

[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct LPRData
{
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string data;

    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
    public int[] prob;
}
2
Zenexer