所以我正在尝试编写一个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()
返回{{1} }作为一个大小,当它应该是52
时,如果我将852
添加到Size=
属性,该函数将“通过”,但我相当确定数据不是正确地通过了
我很确定问题是这个StructLayout
我认为Marshal.StructToPtr()没有正确地编组它,因为它本质上是一个多维数组。
所以我想我的问题甚至可能吗?似乎C#可能足够聪明,知道如何在内存中为该结构数组创建适当数量的空间......但也许不是?
替代方案我认为“可以”起作用。
编写自定义选择器,将对象转换为byte []并返回,零元数据为零。 - 不理想。
是否可以编写一个混合的clr c ++ dll并尝试将其用作楔形。不过我担心的是,我是否会遇到同样的问题,但只是在托管的c ++中?或者甚至在混合模式下,我必须编写一个托管类来包装未管理的对象以在c#中使用它。但问题是如何将其传递到deviceIOcontrol,如果我从c#中做到这一点,而不是当前试图正确编组东西的问题?或者,如果我将它传递给调用DeviceIOControl的C ++调用,那么我需要知道如何获取传入的每个manged对象的非manged类型。
只需编写创建对象的c ++函数并调用deviceIOControl,因为参数可能会失控,所以不太了解?
放弃并用C ++完成所有操作,我实际上是尝试为我的硬件编写单元测试,而VS中的新cpp单元测试确实很好地集成了...
我之前也看到了这个问题,试了一下,不过我认为我的场景有点不同。 Un-/Marshalling nested structures containing arrays of structures
public Points[] p = new Points[10];
更新2 我应该澄清一下,我正在尝试向驱动程序发送一个对象,而不是接收一个(至少还没有)
这就是我的称呼方式。
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];
};
答案 0 :(得分:1)
我真的不知道标题上的内容,但作为起点,请考虑阅读this何时使用 struct 或类强>
作为警报......你确定Pack = 1吗?你有#pragma将它设置为1吗?
如果您提供相关的.h代码,则更容易检查可能出错的内容。无论如何,有了可用的信息,我会这样做:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct VENUS_FORMAT4
{
public uint Top;
public uint Left;
public uint Rows;
public uint Columns;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.MAX_CD_ROWS)]
public uint[] V65Rows;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.MAX_CD_COLS_DD2)]
public uint[] CDCols;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.MAX_DD_SECTIONS)]
public uint[] DDSections;
}
除了 VENUS_VM4_DEVICE_FORMAT4IL 之外,其余部分基本上与上面相同,您必须"复制"这些字段是因为在使用结构时不能继承(在C#(类型值)中)。
此外,如果在C ++方面你有联盟,这不会起作用,你应该使用 LayoutKind.Explicit 和 FieldOffset 。
答案 1 :(得分: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。
如果您想避免在Shape
中重复GeoShape
的定义,可以嵌入它:
public struct GeoShape
{
Shape shape;
int top;
int left;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
int[] v;
};
答案 2 :(得分:0)
我终于能够解决它购买以下两件事。
fixed
关键字。我删除了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];
};