C# вызывающее устройство IOControl со сложными структурами

Поэтому я пытаюсь написать оболочку C# для общения с одним из наших драйверов устройств. (создание модульного теста) Драйвер является новым, но закодирован для старых заголовков C++, поэтому структуры структуры определены и не могут быть изменены.

Итак, я скопировал структуры с ++, которые ожидают от устройства DeviceIOControl.

Обновление № 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#, что будет иметь текущую проблему с попыткой правильно маршалировать вещи? Или, если я передам его в вызов C++, который вызывает DeviceIOControl, то мне нужно знать, как добраться до необработанного типа каждого передаваемого объекта.

  3. Просто напишите функции на языке C++, которые создают объекты, и вызовите deviceIOControl. Меньше идей, поскольку параметры могут выйти из-под контроля?

  4. Сдавайся и делай все это на C++, я на самом деле пытаюсь написать модульный тест для моего оборудования, а более новый модульный тест cpp в VS действительно довольно хорошо интегрируется...

Я также видел этот предыдущий вопрос и попробовал его, однако я думаю, что мой сценарий немного другой. Не / Маршаллинг вложенных структур, содержащих массивы структур

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

3 ответа

Решение

Я на самом деле не знаю, что находится в заголовках, но в качестве отправной точки подумайте о том, когда использовать struct или class.

В качестве предупреждения... вы уверены, что 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.

Поскольку вы встраиваете структуры в структуры, вам нужно будет использовать структуры 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 вещи.

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

Другие вопросы по тегам