0

所以我正在尝试编写一个 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# 可能足够聪明,知道如何在内存中为该结构数组创建适量的空间.. 但也许不是?

我认为“可行”的替代方案。

  1. 编写自定义 serailizer,将对象转换为 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。

如果您想避免重复 in 的定义,Shape可以GeoShape将其嵌入:

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

我真的不知道标题上有什么,但作为起点,请考虑阅读有关何时使用结构类的信息

提醒一下……您确定 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.ExplicitFieldOffset

于 2014-06-24T21:55:02.793 回答
0

我终于能够修复它购买做以下两件事。

  1. 我将我的第一个类更改为结构并使用fixed关键字。
  2. 我删除pack=1StructLayoutAttribute.

    [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 回答