0

そのため、デバイス ドライバーの 1 つと通信する C# ラッパーを作成しようとしています。(単体テストの作成) ドライバーは新しいものですが、古い C++ ヘッダーに対してコーディングされているため、構造レイアウトが定義されており、実際には変更できません。

そのため、デバイスが DeviceIOControl を渡すことを期待している C++ 構造を複製しました。

更新 #3 - コードを同じ問題のあるデモ コードに変更します。また、他の人が使いやすいように質問をクリーンアップします。以下の私の回答を参照してください

[StructLayout(LayoutKind.Sequential, Pack=1)]
public class Points
{
    public int id;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]  
    public int[] x = new int[10];
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    public int[] y = new int[10];
};

[StructLayout(LayoutKind.Sequential, Pack=1)]
public class Shape
{
    public int name;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    public Points[] p = new Points[10];
};

[StructLayout(LayoutKind.Sequential,Pack1)]
public class GeoShape:Shape
{
    public int top;
    public int left;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    public int[] v = new int[10];
};

ドライバー側では、渡されたバッファーのサイズをチェックするため、への呼び出しは失敗します。C# 側では、deviceIOControlオブジェクトが小さすぎてサイズとしてMarshal.SizeOf()返さ52れます。関数は「合格」しますが、データが正しく渡されていないと確信しています。852Size=StructLayout

問題はこれであると確信していpublic Points[] p = new Points[10];ます。これは本質的に多次元配列であるため、Marshal.StructToPtr() がこれを正しくマーシャリングしていないと思います。

それで、私の質問はこれも可能だと思いますか?C# は、その構造体の配列に対してメモリ内に適切な量のスペースを作成する方法を知るのに十分スマートである可能性があるようです..しかし、そうではないでしょうか?

私が考えた代替案は「機能する」可能性があります。

  1. オブジェクトを byte[] に変換して元に戻すカスタム シリアライザーを作成し、メタデータはゼロにします。- 理想的ではありません。

  2. 混合 clr c++ dll を作成し、それをウェッジとして使用することは可能でしょうか。しかし、私の懸念は、同じ問題が発生するだけで、マネージ C++ だけでしょうか? または、混合モードでも、マネージされていないオブジェクトをラップして C# で使用するマネージ クラスを作成する必要があります。しかし、問題はそれをdeviceIOcontrolに渡す方法になります.C#からそれを行うと、物事を正しくマーシャリングしようとするという現在の問題が発生しますか? または、DeviceIOControl を呼び出す C++ 呼び出しに渡す場合、渡された各管理対象オブジェクトの管理対象外の型を取得する方法を知る必要があります。

  3. オブジェクトを作成して deviceIOControl を呼び出す C++ 関数を書くだけで、パラメーターが制御不能になる可能性があるため、あまり考えられませんか?

  4. あきらめて、すべて C++ で行います。実際にハードウェアの単体テストを作成しようとしていますが、VS の新しい cpp 単体テストはかなりうまく統合されています...

私もこの前の質問を見て試してみましたが、私のシーンは少し違うと思います。構造体の配列を含むネストされた構造体のアン/マーシャリング

 struct Points
{
    int id;
    int x[10];
    int y[10];
};


struct Shape
{
    int name;
    Points p[10];
};

struct GeoShape :Shape
{
    int top;
    int left;
    int v[10];
};

更新 2 オブジェクトをドライバーに送信しようとしていることを明確にする必要があります (少なくともまだ)。

これが私がそれを呼んでいる方法です。

public static bool SetObject(SafeFileHandle device, DeviceControlCode ioctlCode, Object obj)
        {
            int size = Marshal.SizeOf(obj.GetType());
            IntPtr ptr = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(obj, ptr, false);

            // call the dviceIOControl method
            return Control(device, ref ioctlCode, ptr, size, IntPtr.Zero, 0);
        }
4

3 に答える 3

1

構造体内に構造体を埋め込むため、構造体が参照ではなく値になるように、クラスではなく C# 構造体を使用する必要があります。これは、継承の使用をあきらめなければならないことを意味します。次のように構造を翻訳できます。

public struct Points
{
    int id;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    int[] x;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    int[] y;
};

public struct Shape
{
    int name;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    Points[] p;
};

public struct GeoShape
{
    int name;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    Points[] p;
    int top;
    int left;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    int[] v;
};

これらの定義でMarshal.SizeOf(typeof(GeoShape))は、892 と評価されます。正しい値は 852 であると主張していますが、そうではないことに注意してください。C++ 構造体のサイズは 892 です。

Shapeinの定義を繰り返さないようにしたい場合は、次のGeoShapeように埋め込むことができます。

public struct GeoShape
{
    Shape shape;
    int top;
    int left;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    int[] v;
};
于 2014-06-26T09:13:34.817 に答える
0

次の2つのことを実行して、最終的に修正することができました。

  1. 最初のクラスを構造体に変更し、fixedキーワードを使用しました。
  2. pack=1の値を削除しましたStructLayoutAttribute

    [StructLayout(LayoutKind.Sequential)] public struct Points { public int id; public unsafe fixed int x[10]; public unsafe fixed int y[10]; //[MarshalAs(UnmanagedType.ByValArray, SizeConst=10)] //public int[] x = new int[10]; //[MarshalAs(UnmanagedType.ByValArray, SizeConst=10)] //public int[] y = new int[10]; };

于 2014-06-25T19:11:20.563 に答える