13

私はこの構造体とこのコードを持っています:

[StructLayout(LayoutKind.Sequential, Pack = 8)]
private class xvid_image_t
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public int[] stride;

    // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    // public IntPtr[] plane;
}

public int decore()
{
    xvid_image_t myStruct = new xvid_image_t();
    myStruct.stride = new int[4]; // can be commented out - same result
    GCHandle.Alloc(myStruct, GCHandleType.Pinned);

    // ...
}

実行しようとすると、次のようにArgumentException言います。

オブジェクトに非プリミティブまたは非 blittable データが含まれている

このMSDNページを読んだ後、

次の複合型も blittable 型です。

  • 整数の配列など、blittable 型の 1 次元配列。ただし、blittable 型の可変配列を含む型自体は blittable ではありません。

  • blittable 型 (およびフォーマットされた型としてマーシャリングされている場合はクラス) のみを含む、フォーマットされた値の型。フォーマットされた値の型の詳細については、「値の型の既定のマーシャリング」を参照してください。

私が間違っていることを理解していません。を使いたいだけMarshalでなく、これも理解したい。

だから私が実際に知りたいのは:

  1. なんで?
  2. どうすればこれを解決できますか?
  3. あなたが提供するソリューションは、構造体のコメント行でも機能しますか?

.Net 4.5 を使用していますが、.Net 2.0 のソリューションも必要です。

4

3 に答える 3

16

オブジェクトに非プリミティブまたは非 blittable データが含まれている

それが例外メッセージです。メッセージの「非 blittable」部分に注目していますが、それは問題ではありません。問題なのは「非プリミティブ」部分です。配列は非プリミティブ データ型です。

CLR は、ここで問題を回避しようとしています。オブジェクトを固定できますが、それでも問題があり、配列は固定されません。ピン留めする必要があるフィールドがオブジェクトにある場合、そのオブジェクトは実際にはピン留めされていません。

また、構造変換が必要な UnmanagedType.ByValArray には、より大きな問題があります。つまり、必要なレイアウトは、マネージ クラス オブジェクトのレイアウトとはまったく異なります。この変換を行うことができるのは pinvoke マーシャラーだけです。

fixedキーワードを使用して、固定サイズのバッファーを使用することにより、pinvoke マーシャラーを使用せずに必要なものを取得できます。これにはunsafeキーワードを使用する必要があります。次のようにします。

    [StructLayout(LayoutKind.Sequential)]
    unsafe private struct xvid_image_t {
        public fixed int stride[4];
    }

宣言を構造体型に変更する必要があることに注意してください。これは値型になりました。値をローカル変数にするときに、GCHandle を使用して値を固定する必要がなくなりました。通常は参照によって構造体の値を取得するアンマネージ コードが構造体へのポインターを格納しないようにしてください。それはひどく爆発し、まったく診断不能になります。ここではunsafeキーワードが適切です。ポインターを格納する場合は、弾丸をバイトし、Marshal.AllocHGlobal() と Marshal.StructureToPtr() を使用して、アンマネージ コードがポインターを使用している間、ポインターが有効なままであることを確認する必要があります。

于 2013-03-21T14:15:37.360 に答える
4

.NETの厄介な制限は、.NETが認識する配列っぽいものは、スタンドアロンSystem.Arrayオブジェクトと。でありSystem.String、どちらも参照型であるということです。C#で記述されたコードでfixed配列を使用することは可能ですが(Hans Passantによると)、そのようなタイプは.NET自体では認識されず、固定配列を使用するコードは検証できません。さらに、固定配列はプリミティブの保持に制限されており、vb.netなどの他の言語からはアクセスできません。

固定配列を使用する2つの方法は、次のとおりです。

  • 固定配列を、合計が適切なサイズになるフィールドの組み合わせに置き換えます(ほとんどの場合、N個の変数を使用しますが、おそらくachar[4]をaUInt32に、またはachar[8]をに置き換えUInt64ます)。配列が大きすぎない場合は、(カット/ペーストまたはリフレクションを介して)refによって構造体を取得し、適切な要素を読み取り/書き込みする一連の静的メソッドを定義してから、そのようなメソッドを呼び出すためのデリゲートの配列を作成できます。 。

  • 構造全体を配列に置き換えてから、その配列の最初の要素をrefパラメーターとして渡します。これは、構造内で配列を使用するよりもさらに「危険」な場合がありますfixedが、vb.netで、実際にアクセスする必要のあるものを含む構造で「pass-by-ref」セマンティクスを取得する唯一の方法です。配列として。

値型配列は「混乱する」と見なされた可能性があることは理解できますが(特に自動ボックス化された場合)、パスを許可するという観点から、配列ストレージの意味的に正しいアプローチであった場所があります。 COM相互運用機能の参照によるセマンティクス、および少数の値を返すことになっているメソッドの観点からも。たとえば、System.Drawing2dには、現在のグラフィック変換を次のように返すメソッドがあります。float[6]; 実験以外では、返された後のその配列への変更が他に影響を与えるか、影響を与える可能性があるか、または影響を与えないことが保証されているかどうかを知る明確な方法はありません。メソッドが値型配列を返した場合、返された配列への変更が他に影響を与えることはないことは明らかです。それにもかかわらず、値型配列がフレームワークの有用な部分であったかどうかにかかわらず、良い理由であろうと悪い理由であろうと、そのようなものは存在しないという事実は残っています。

于 2013-03-21T14:50:50.010 に答える