c#调用复杂结构的deviceIOControl

本文关键字:deviceIOControl 结构 复杂 调用 | 更新日期: 2023-09-27 18:10:04

所以我正在尝试写一个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. 编写自定义序列化器,将对象转换为字节[]并返回,零元数据。-不理想。

  2. 是否有可能编写一个混合clr/c++ dll并尝试使用它作为楔子?然而,我的担忧,我只是会有同样的问题,但只是在托管c++?或者即使在混合模式下,我也必须编写一个托管类来包装非托管对象,以便在c#中使用它。但问题是如何将其传递到deviceIOcontrol,如果我从c#中这样做,那么当前的问题将是试图正确地编组内容?或者,如果我将它传递给调用DeviceIOControl的c++调用,那么我需要知道如何获得传入的每个托管对象的非托管类型。

  3. 只是写c++函数来创建对象和调用deviceIOControl,更少的想法,因为参数可能会失控?

  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);
        }

c#调用复杂结构的deviceIOControl

我真的不知道头文件上有什么,但作为一个起点,考虑一下何时使用structclass

作为一个警告…你确定是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。明确的 FieldOffset

因为你是在结构中嵌入结构,你将需要使用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.

如果你想避免在GeoShape中重复Shape的定义,你可以嵌入它:

public struct GeoShape
{
    Shape shape;
    int top;
    int left;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
    int[] v;
};

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

  1. 我改变了我的第一个类是一个结构体,并使用fixed关键字。
  2. 我删除了StructLayoutAttribute上的pack=1值。

    [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]; };