C#调用具有复杂结构的deviceIOControl

时间:2014-06-24 21:21:56

标签: c# c++ pinvoke marshalling deviceiocontrol

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

替代方案我认为“可以”起作用。

  1. 编写自定义选择器,将对象转换为byte []并返回,零元数据为零。 - 不理想。

  2. 是否可以编写一个混合的clr c ++ dll并尝试将其用作楔形。不过我担心的是,我是否会遇到同样的问题,但只是在托管的c ++中?或者甚至在混合模式下,我必须编写一个托管类来包装未管理的对象以在c#中使用它。但问题是如何将其传递到deviceIOcontrol,如果我从c#中做到这一点,而不是当前试图正确编组东西的问题?或者,如果我将它传递给调用DeviceIOControl的C ++调用,那么我需要知道如何获取传入的每个manged对象的非manged类型。

  3. 只需编写创建对象的c ++函数并调用deviceIOControl,因为参数可能会失控,所以不太了解?

  4. 放弃并用C ++完成所有操作,我实际上是尝试为我的硬件编写单元测试,而VS中的新cpp单元测试确实很好地集成了...

  5. 我之前也看到了这个问题,试了一下,不过我认为我的场景有点不同。 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];
    };
    

3 个答案:

答案 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)

我终于能够解决它购买以下两件事。

  1. 我将第一个类改为结构并使用了fixed关键字。
  2. 我删除了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]; };