そのため、デバイス ドライバーの 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
れます。関数は「合格」しますが、データが正しく渡されていないと確信しています。852
Size=
StructLayout
問題はこれであると確信していpublic Points[] p = new Points[10];
ます。これは本質的に多次元配列であるため、Marshal.StructToPtr() がこれを正しくマーシャリングしていないと思います。
それで、私の質問はこれも可能だと思いますか?C# は、その構造体の配列に対してメモリ内に適切な量のスペースを作成する方法を知るのに十分スマートである可能性があるようです..しかし、そうではないでしょうか?
私が考えた代替案は「機能する」可能性があります。
オブジェクトを byte[] に変換して元に戻すカスタム シリアライザーを作成し、メタデータはゼロにします。- 理想的ではありません。
混合 clr c++ dll を作成し、それをウェッジとして使用することは可能でしょうか。しかし、私の懸念は、同じ問題が発生するだけで、マネージ C++ だけでしょうか? または、混合モードでも、マネージされていないオブジェクトをラップして C# で使用するマネージ クラスを作成する必要があります。しかし、問題はそれをdeviceIOcontrolに渡す方法になります.C#からそれを行うと、物事を正しくマーシャリングしようとするという現在の問題が発生しますか? または、DeviceIOControl を呼び出す C++ 呼び出しに渡す場合、渡された各管理対象オブジェクトの管理対象外の型を取得する方法を知る必要があります。
オブジェクトを作成して deviceIOControl を呼び出す C++ 関数を書くだけで、パラメーターが制御不能になる可能性があるため、あまり考えられませんか?
あきらめて、すべて 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);
}