所以我正在尝试编写一个 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];
};
我的调用deviceIOControl
失败,因为在驱动程序端它检查传入的缓冲区的大小。在 C# 端,我的对象太小,无法Marshal.SizeOf()
返回52
大小,当它应该是时852
,如果我添加Size=
到StructLayout
属性,该函数将“通过”,但我相当确定数据未正确传递。
我相当确定问题是public Points[] p = new Points[10];
我认为 Marshal.StructToPtr() 没有正确编组它,因为它本质上是一个多维数组。
所以我想我的问题可能吗?似乎 C# 可能足够聪明,知道如何在内存中为该结构数组创建适量的空间.. 但也许不是?
我认为“可行”的替代方案。
编写自定义 serailizer,将对象转换为 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);
}