С#, вызывающий deviceIOControl со сложными структурами

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

Поэтому я воспроизвел структуры 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 терпит неудачу, потому что на стороне драйвера он проверяет размер переданного буфера. На стороне С# мой объект слишком мал, так как Marshal.SizeOf() возвращает 52 в качестве размера, когда он должен быть 852, если я добавлю Size= в атрибут StructLayout, функция "пройдёт", но я уверен, что данные передаются неправильно.

Я совершенно уверен, что проблема в этом public Points[] p = new Points[10]; Я думаю, что Marshal.StructToPtr() неправильно маршалирует это, поскольку по сути это многомерный массив.

Поэтому я предполагаю, что мои вопросы вообще возможны? Кажется, что C# может быть достаточно умным, чтобы знать, как создать правильный объем памяти для этого массива структуры... но, может быть, нет?

Альтернативы, которые я думал о том, что «могло бы» работать.

  1. Напишите собственные serailizers, которые преобразуют объект в byte[] и обратно с нулевыми метаданными. - Не идеально.

  2. Можно ли написать смешанную clr c++ dll и попытаться использовать ее как клин. Однако мои опасения, что у меня будет такая же проблема, но только в управляемом С++? Или даже в смешанном режиме мне пришлось бы написать управляемый класс, чтобы обернуть неуправляемый объект, чтобы использовать его в С#. Но проблема заключается в том, как передать это в deviceIOcontrol, если я сделаю это из С#, чем возникнет текущая проблема с попыткой правильно маршалировать материал? Или, если я передам его в вызов C++, который вызывает DeviceIOControl, тогда мне нужно знать, как получить неуправляемый тип каждого переданного управляемого объекта.

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

  4. Откажитесь и сделайте все это на С++, я на самом деле пытаюсь написать модульный тест для своего оборудования, и более новый модульный тест 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);
        }

person nagates    schedule 24.06.2014    source источник
comment
Невозможно сказать, где вы ошиблись, учитывая, что вы не опубликовали объявления C++. Конечно, вам следует рассмотреть C++/CLI, он может напрямую читать файл .h. С ненулевыми шансами вы узнаете об этом достаточно, чтобы иметь возможность писать правильные объявления C#.   -  person Hans Passant    schedule 25.06.2014
comment
Что ж, я совершенно уверен, что проблема в том, что моя структура С# не имеет того же размера, когда я вызываю С# marshal.Sizeof() с моей структурой, я получаю размер 156, когда он должен быть 7776. В основном С# не знает, как правильно выстроить структуру.   -  person nagates    schedule 25.06.2014
comment
@Han Passant Я играл с этим безгранично, но я не думаю, что C # может видеть эти неуправляемые структуры, и если бы это было так, как бы я вызвал DeviceIOControl без необходимости снова выполнять какой-то Marshal.   -  person nagates    schedule 25.06.2014
comment
Догадаться! скоро опубликую ответ.   -  person nagates    schedule 25.06.2014


Ответы (3)


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

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

person tweellt    schedule 24.06.2014
comment
Я мог перейти от класса к структуре. Я не думаю, что это проблема? Я также должен отметить, что у меня работал еще один вызов IOCTL, но он имел НАМНОГО более простую структуру. Уверен ли я на 100% в Pack, нет, но он работал у меня раньше. Я думаю, что я свободен от профсоюзов. Мне придется подумать о размещении содержимого заголовка, это довольно длинно и беспорядочно. - person nagates; 25.06.2014
comment
@NateGates, для начала вы можете публиковать только код, связанный с этими структурами. У него вообще есть пакет #pragma? Потому что это могло работать раньше, но это зависит от выравнивания упаковки на стороне С++. - person tweellt; 25.06.2014
comment
Вы спрашиваете, был ли у С++ пакет прагмы? Я все еще думаю, что проблема в том, что мой Marshal.SizeOf возвращает (156 против 7776) другой размер структуры, чем код С++. И мой драйвер использует этот размер, чтобы убедиться, что объект является структурой. Я совершенно уверен, что С# просто не знает, как правильно организовать мою структуру. - person nagates; 25.06.2014
comment
Насколько я могу судить, мы не делаем никаких pragma pack в заголовочном файле. - person nagates; 25.06.2014
comment
@NateGates, помимо того, что я написал, я не вижу никаких проблем. Возможно, вы захотите рассмотреть c++\cli, как предложил Ханс Пассант. - person tweellt; 25.06.2014

Поскольку вы встраиваете структуры внутрь структур, вам нужно будет использовать структуры 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;
};
person David Heffernan    schedule 26.06.2014

Наконец-то я смог исправить это, выполнив следующие две вещи.

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

person nagates    schedule 25.06.2014
comment
Здесь не нужно исправлять. Без него легко обойтись, и это значительно упрощает вашу жизнь. - person David Heffernan; 26.06.2014
comment
Исправление - единственный способ заставить его работать. Что бы вы еще предложили? - person nagates; 26.06.2014
comment
Что ж, с таким отношением я сдаюсь. Неинтересно помогать тому, кто так начинает. Делай по-своему. - person David Heffernan; 26.06.2014
comment
Что ж, легко избежать исправленного, но вы решили, что это невозможно, и отчитали меня за то, что я не прочитал вопрос. - person David Heffernan; 26.06.2014
comment
Извините, я просто расстроен, потому что чувствую, что люди не слушают. Но если вы решите не помогать, на данный момент я мало что могу сделать, кроме как извиниться за свою грубость. - person nagates; 26.06.2014
comment
Избавьтесь от Pack, используйте struct и UnmanagedType.ByValArray. Макет такой же, как фиксированный. - person David Heffernan; 26.06.2014
comment
Я думал, что пробовал это .. но я могу попробовать еще раз. Нужно ли использовать структуру во всех моих классах? - person nagates; 26.06.2014
comment
Да. Вам нужны значения, а не ссылки. - person David Heffernan; 26.06.2014
comment
Хм, я надеялся избежать этого, так как это означает, что для имитации наследования мне нужно либо сгладить объект, либо объявить переменные других структур. Утром сделаю, спасибо за помощь. - person nagates; 26.06.2014
comment
Есть ли в этом преимущество над фиксированным. Я знаю, что фиксированный синтаксис не так хорош. - person nagates; 26.06.2014
comment
Получает ли Marshal.SizeOf размер ссылки, а не самого объекта? - person nagates; 26.06.2014
comment
@NateGates, Marshal.SizeOf получает размер объекта, поэтому вы либо передаете базовый тип, либо сам созданный объект. Что касается вашего решения, я немного сбит с толку, так как я написал это в своем ответе, и я думал, что вы его протестировали, включая мои сомнения по поводу пакета. - person tweellt; 26.06.2014
comment
@tweelt, похвала там, где это необходимо, вы правы, сомневаясь в пакете и, возможно, в структурах. Аналогично работало с предыдущим вызовом IOCTL, который я реализовал, и я нашел его во многих других примерах, поэтому я ему доверял. Кроме того, мне показалось, что документация по стоимости пакета имеет смысл. При этом, почему, ребята, проголосовали против? Мой ответ технически правильный и всего лишь подмножество ответа Дэвида. - person nagates; 27.06.2014
comment
@NateGates, голосование против было не моим, но я подозреваю, что вы его получили, потому что написали, что исправление помогло решить вашу проблему, которую ByValArray и SizeConst должны делать точно так же без небезопасного кода. Возможно, отредактируйте этот пост и напишите решение в понятной форме, чтобы другие могли извлечь из этого пользу. Ваше здоровье - person tweellt; 27.06.2014
comment
@tweellt Нет, я не думал, что это ты. Но кто бы это ни сделал, я думаю, что он изо всех сил старался проголосовать против пары моих старых постов (проголосовали против одновременно). Раньше мне нравился stackoverflow, но я боюсь, что он становится враждебным сайтом. В любом случае, спасибо за ваш ответ и помощь, я должен был выслушать вас раньше. - person nagates; 28.06.2014