11

ネイティブ DLL からエクスポートされた、次のような純粋な C インターフェイスを持つネイティブ関数があるとします。

// NativeDll.cpp

extern "C" void __stdcall FillArray(
    int fillValue, 
    int count, 
    int* data)
{
    // Assume parameters are OK...

    // Fill the array
    for (int i = 0; i < count; i++)
    {
        data[i] = fillValue;
    }
}

次の P/Invoke は正常に動作します (VS2010 SP1 でテスト済み)。

[DllImport("NativeDll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern void FillArray(
    int fillValue,
    int count,
    [In, Out] int[] data
);

この P/Invoke と同様に、上記と同じですが、属性はありません:[In, Out]

[DllImport("NativeDll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern void FillArray(
    int fillValue,
    int count,
    int[] data
);

では、これらの[In, Out]属性は配列をマーシャリングするためのオプションですか? もしあれば、彼らの目的は何ですか?P/Invoke 宣言でそれらを省略してもよろしいですか?

4

1 に答える 1

21

いいえ、厳密にはオプションではありません。たまたまうまくいくだけです。しかし、それは非常に一般的な事故です。配列が実際にマーシャリングされないため、機能します。pinvoke マーシャラーは、C# 配列が既にネイティブ配列と互換性があることを認識しているため、そのコピーを作成する手順をスキップします。単に配列を固定し、ポインターをネイティブ コードに渡します。

もちろん、これは非常に効率的であり、ネイティブ コードが配列要素を直接書き込んでいるため、必然的に結果が返されます。したがって、[In] 属性も [Out] 属性も関係ありません。

配列要素の型がそれほど単純でない場合は、さらに複雑になります。blittable でない構造体型またはクラス型である要素型、またはマーシャリング後にレイアウトが一致しない要素型を識別するのはそれほど簡単ではないため、pinvoke マーシャラー配列のコピーを作成する必要があります。特に、マネージド レイアウトは検出できないため、レイアウトの非互換性を特定するのは非常に困難です。また、使用されるジッターによって変化する可能性があります。x86 では動作する可能性がありますが、x64 では動作しません。たとえば、AnyCPU が選択されているとかなり厄介です。変更されたコピーを C# 配列にコピーして戻すには、[Out]が必要です。

何をアドバイスすればよいかわかりませんが、宣言で明示的であることを理由に解雇された人は誰もいません。おそらく、配列要素の型が単純ではない場合は常に明示する必要があるため、事故が発生することはありません。

于 2013-01-16T19:39:43.990 に答える