Как инициализировать небезопасный указатель в C # и преобразовать его в байт []? - PullRequest
2 голосов
/ 09 декабря 2010

Вчера я разместил пост. Как создать структуры для C #, изначально написанные на C ++.

Спасибо за ваши ответы.

Я пытаюсь, но без особого успеха,использовать DeviceIOControl на платформе ARM под управлением WinCE 6.0 и .NET Compact framework 2.0. Все, чего я пытаюсь добиться, - это управление выводом порта, и это становится кошмаром.

Ниже приводится объявление PInvoke:

[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool DeviceIoControlCE(int hDevice,
                                             int dwIoControlCode,
                                             byte[] lpInBuffer,
                                             int nInBufferSize,
                                             byte[] lpOutBuffer,
                                             int nOutBufferSize,
                                             ref int lpBytesReturned,
                                             IntPtr lpOverlapped);

Объявление PInvoke предполагает, что ему может быть просто передан байт [].Конечно, легко записать значения для каждого члена структуры, преобразовать его в массив байтов и передать его в dll.

У меня есть следующее:

    [StructLayout(LayoutKind.Sequential)]  
    public struct pio_desc 
    {
        unsafe byte* pin_name;      //Length???
        public uint pin_number;     //4 bytes
        public uint default_value;  //4 bytes
        public byte attribute;      //1 byte
        public uint pio_type;       //4 bytes
    }

и

pio_desc PA13 = new pio_desc ();

Так что теперь, безусловно, нужно сделать что-то вроде:

PA13.pin_number = AT91_PIN_PA13;      //Length 4 bytes
PA13.default_value = 0;               //Length 4 bytes
PA13.attribtue = PIO_DEFAULT;         //Length 1 byte
PA13.pio_type = PIO_OUTPUT;           //Length 4 bytes

и преобразовать (например, pin_number) вбайт []:

byte[] temp = BitConverter.GetBytes(PA13.pin_number); //uints are 4 bytes wide
        byteArray[++NumberOfChars] = temp[0];
        byteArray[++NumberOfChars] = temp[1];
        byteArray[++NumberOfChars] = temp[2];
        byteArray[++NumberOfChars] = temp[3];     //Will need to check on Endianess

Вопросы:

Как в структуре PA13 инициализировать небезопасный указатель pin_name?Автор драйвера отмечает, что он не используется, предположительно, его водителем.Потребуется ли для Windows какое-либо значение?

PA13.pin_name = ??????

Затем, как мне преобразовать этот указатель в байт, чтобы поместиться в мой массив byte []быть переданным в DeviceIOControl?

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

Еще раз спасибо залюбая (простая) помощь.

Ответы [ 3 ]

1 голос
/ 09 декабря 2010

Вам нужно будет сделать несколько разных вещей здесь. Сначала замените этот элемент:

unsafe byte* pin_name;      //Length???

с:

[MarshalAs(UnmanagedType.LPStr)] public string pin_name;

Затем замените входные / выходные буферы в объявлении P / Invoke с byte[] на IntPtr. Затем вы можете использовать этот код для преобразования данных:

pio_desc PA13;
// Set the members of PA13...

IntPtr ptr = IntPtr.Zero;
try {
    var size = Marshal.SizeOf(PA13);
    ptr = Marshal.AllocHGlobal(size);

    Marshal.StructureToPtr(PA13, ptr, false);

    // Your P/Invoke call goes here.
    // size will be the "nInBufferSize" argument
    // ptr will be the "lpInBuffer" argument
} finally {
    if (ptr != IntPtr.Zero) {
        Marshal.DestroyStructure(ptr, typeof(pio_desc));
        Marshal.FreeHGlobal(ptr);
    }
}
1 голос
/ 09 декабря 2010

Вы можете сделать это намного проще, обманывая объявление [DllImport]. Просто объявите аргумент lpInBuffer как тип структуры, маршаллер pinvoke все равно преобразует его в указатель. Таким образом:

[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool SetOutputPin(IntPtr hDevice,
                                             int dwIoControlCode,
                                             ref pio_desc lpInBuffer,
                                             int nInBufferSize,
                                             IntPtr lpOutBuffer,
                                             int nOutBufferSize,
                                             out int lpBytesReturned,
                                             IntPtr lpOverlapped);

Использование IntPtr для lpOutBuffer, потому что драйвер, вероятно, ничего не возвращает. Пройдите IntPtr.Zero. Та же идея со структурой. Если поле не используется, просто объявите его как IntPtr:

[StructLayout(LayoutKind.Sequential)]  
public struct pio_desc 
{
    public IntPtr pin_name;     // Leave at IntPtr.Zero
    public uint pin_number;     //4 bytes
    public uint default_value;  //4 bytes
    public byte attribute;      //1 byte
    public uint pio_type;       //4 bytes
}

Будьте осторожны со свойством Packing, оно имеет значение здесь из-за размера поля в байтах. Вам может понадобиться 1, но это всего лишь предположение, ничего не зная о водителе. Если у вас есть работающий код на C, протестируйте значение sizeof (pio_desc) и сравните с Marshal.SizeOf (). Передайте Marshal.SizeOf (typeof (pio_desc)) в качестве аргумента nInBufferSize. Если бы вы опубликовали декларации C, на этот вопрос было бы проще ответить точно.

0 голосов
/ 09 декабря 2010

Объявите lpInBuffer и lpOutBuffer как IntPtr. Инициализируйте их с помощью Marshal.AllocHGlobal (не забудьте в конце выпустить их с Marshal.FreeHGlobal). Заполните этот буфер и прочитайте его, используя различные перегрузки Marshal.Copy.

...