【发布时间】:2014-08-15 07:12:42
【问题描述】:
所以我正在尝试编写一个 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];
};
我对@987654323@ 的调用失败,因为在驱动程序端它检查传入缓冲区的大小。在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 单元测试确实可以很好地集成...
我也看到了这个较早的问题,并尝试了一下,但我认为我的场景有点不同。 Un-/Marshalling nested structures containing arrays of structures
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);
}
【问题讨论】:
-
鉴于您没有发布 C++ 声明,因此无法判断您哪里出错了。当然你应该考虑C++/CLI,它可以直接读取.h文件。以非零的几率,您了解它足以编写正确的 C# 声明。
-
好吧,我很确定问题是我的 C# 结构大小不一样,当我用我的结构调用 C# marshal.Sizeof() 时,我得到的大小是 156,而它应该是 7776。基本上 c# 不知道如何正确编组结构。
-
@Han Passant 我玩弄了这个无限位,但我不认为 C# 可以看到那些非托管结构,如果它看到了,我将如何调用 DeviceIOControl 而无需再次执行某种 Marshal .
-
想通了!很快就会发布答案。
标签: c# c++ pinvoke marshalling deviceiocontrol