PInvoke, указатели и копирование массива - PullRequest
5 голосов
/ 04 февраля 2011

Мы создаем приложение на c #, .net 4.0, на Win7 x64 с таргетингом на x32.

Мы используем стороннюю библиотеку в нашем приложении. Мы понимаем, что эта библиотека написана с использованием C ++. Однако, чтобы позволить разработчикам на c # использовать эту библиотеку, они обернули ее с помощью P / Invoke, поэтому мы так называем функции API.

Один из вызовов API выглядит следующим образом:

ReadFromDevice(int deviceAddress, int numBytes, Byte[] data);

Эта функция считывает numBytes данных с внешнего устройства и помещает их в data []. Как вы можете видеть, он ожидает увидеть байтовый массив C # в качестве третьего аргумента. Теперь наша проблема в том, что мы хотим прочитать данные в произвольном месте в предварительно объявленном массиве. Например:

Byte[] myData = new Byte[1024*1024*16];
ReadFromDevice(0x100, 20000, &myData[350]) // Obviously not possible in C#

Если бы мы использовали C / C ++, это было бы тривиально. Учитывая, что базовый API написан на C ++, я чувствую, что мы должны быть в состоянии сделать это и в C #, однако я не могу понять, как это сделать в C #. Может быть, мы можем как-то вызвать базовую библиотеку не через предоставленный интерфейс P / Invoke и написать собственный интерфейс?

Любые идеи будут оценены.

С уважением,

Ответы [ 5 ]

3 голосов
/ 04 февраля 2011

Хотя остальные ответы здесь близки, ни один не является вполне полным.

Сначала вам просто нужно объявить собственное объявление p / invoke.Это самая приятная вещь в p / invoke;Нет только одного способа сделать это.

[DllImport("whatever.dll")]
unsafe extern static void ReadFromDevice(int deviceAddress, int numBytes, byte* data);

Теперь вы можете позвонить с помощью

unsafe static void ReadFromDevice(int deviceAddress, byte[] data, int offset, int numBytes)
{
    fixed (byte* p = data)
    {
        ReadFromDevice(deviceAddress, numBytes, p + offset);
    }
}
3 голосов
/ 04 февраля 2011

Вы можете использовать указатели, но требуется для сборки с проверенным небезопасным режимом.

Byte[] myData = new Byte[1024*1024*16];
fixed( Byte * pB = &myData[350])
{
   ReadFromDevice(0x100, 20000,pB  ) 
} 

Но сначала вам нужно изменить подпись экспортируемого метода на.

ReadFromDevice(int deviceAddress, int numBytes, Byte * data);
2 голосов
/ 04 февраля 2011

Вы все еще можете выполнять арифметику указателей в c #.

Если вы повторно объявите, что импортируете dll (в библиотеку C ++), чтобы использовать IntPtr для передачи байтового массива, это можно увеличить на 350 с помощью метода Add (это фактически возвращает новый intptr)

Здесь есть пример чего-то похожего: http://msdn.microsoft.com/en-us/library/system.intptr.add.aspx#Y811

0 голосов
/ 04 февраля 2011

Решение этой проблемы кажется очевидным.

Хотя это правда, вы можете просто создать свой собственный класс-обертку и DllImport и написать свой собственный класс-обертку, но решение проще, чем это.

ReadFromDevice(int deviceAddress, int numBytes, Byte[] data); 

В контексте ваших сообщений данные являются выходным параметром. Таким образом, решением было бы отправить ему пустой байтовый массив соответствующего размера, а затем поместить заполненный массив в существующий массив.

Все, что вам нужно сделать, это определить размер возвращаемого массива, который вы знали заранее, затем заполнить и заменить.

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

0 голосов
/ 04 февраля 2011

Так просто:

ReadFromDevice(int deviceAddress, int numBytes, ref byte data);

...


Byte[] myData = new Byte[1024*1024*16];
ReadFromDevice(0x100, 20000, ref myData[350]) // Obviously possible in C#
...