2011-02-04 12 views
5

Estamos creando una aplicación en C#, .net 4.0, en Win7 x64, dirigida a x32.PInvoke, punteros y copia de matriz

Estamos utilizando una biblioteca de terceros en nuestra aplicación. Entendemos que esta biblioteca está escrita usando C++. Sin embargo, para permitir que los desarrolladores de C# usen esta biblioteca, la han envuelto usando P/Invoke, así es como llamamos a las funciones API.

Una de las llamadas a la API es el siguiente:

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

Esta función lee numBytes de datos desde un dispositivo externo y lo coloca en datos []. Como puede ver, espera ver una matriz C Byte como el tercer argumento. Ahora, nuestro problema es que nos gustaría leer los datos en una ubicación arbitraria en una matriz predeclarada. Por ejemplo:

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

Si estuviéramos utilizando C/C++, esto sería trivial. Dado que la API subyacente está escrita en C++, creo que también deberíamos poder hacer esto en C#, sin embargo, no puedo encontrar la manera de hacerlo en C#. ¿Tal vez de alguna manera podemos llamar a la biblioteca subyacente no a través de la interfaz P/Invoke suministrada y escribir una interfaz personalizada?

Cualquier idea sería apreciada.

Saludos,

Respuesta

3

Mientras que las otras respuestas aquí están cerca, ninguna es bastante completa.

Primero, solo tiene que declarar su propia declaración de p/invoke. Esto es lo dulce de p/invoke; Nunca hay una sola forma de hacerlo.

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

Ahora se puede llamar con

unsafe static void ReadFromDevice(int deviceAddress, byte[] data, int offset, int numBytes) 
{ 
    fixed (byte* p = data) 
    { 
     ReadFromDevice(deviceAddress, numBytes, p + offset); 
    } 
} 
2

que todavía son capaces de realizar la aritmética de punteros en C#.

Si se vuelve a declarar la importación DLL (en contra de la biblioteca de C++) para utilizar un IntPtr pasar la matriz de bytes, esto puede ser incrementado en un 350 usando el método Add (En realidad, esto devuelve una nueva IntPtr)

Hay un ejemplo de algo similar aquí: http://msdn.microsoft.com/en-us/library/system.intptr.add.aspx#Y811

3

Puede usar punteros pero requiere compilar con el modo inseguro marcado.

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

Pero primero debe cambiar la firma del método exportado a.

ReadFromDevice(int deviceAddress, int numBytes, Byte * data); 
+0

No hay conversión de bytes '*' 'a byte [] ', este código ni siquiera compilar. Si él tuviera control sobre la fuente de C#, eso sería una cosa, pero no suena como lo hace a partir de la pregunta original. –

+0

Dicho esto, al leer nuevamente la pregunta * suena * como si el OP tuviera acceso también a la biblioteca C++, por lo que simplemente debería sugerirle que agregue su propio DllImport y cambie la firma para aceptar un 'byte *' en lugar de un 'byte []', entonces esto funcionaría. Eliminando downvote. –

+0

" Estamos creando una aplicación en C#, .net 4.0, en Win7 x64, segmentando x32. Estamos utilizando una biblioteca de terceros en nuestra aplicación. Entendemos que esta biblioteca está escrita usando C++. Sin embargo, para dejar que C# los desarrolladores usan esta biblioteca, la han envuelto usando P/Invoke, así es como llamamos a las funciones API ". ¡Así que tienen control sobre C# y están escribiendo un contenedor para una biblioteca de C++! – N4rk0

0

Tan sencillo como esto:

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# 
+0

Pero ha cambiado la declaración de ReadFromDevice(). No tengo ese código; que proviene de la biblioteca de terceros. – SomethingBetter

+0

si es .net siempre tienes el código xD. Esto parece un dllimport solo enciende el reflector y copia las 2 líneas en tu aplicación. – Zotta

+0

La biblioteca de terceros ha proporcionado tanto el archivo de compilación C++ (obviamente no se puede cambiar) como una clase contenedora C#. Por favor vea mi respuesta para una solución obvia. –

0

La solución a este problema parece clase de obvio.

Si bien es cierto que usted podría simplemente crear su propia clase contenedora y DllImport y escribir su propia clase contenedora, la solución es más fácil que eso.

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

En el contexto de su publicación, los datos son un parámetro de salida. Por lo tanto, la solución sería enviarle una matriz de bytes vacía del tamaño apropiado y luego colocar la matriz completa en la matriz existente.

Todo lo que tiene que hacer es determinar el tamaño de la matriz devuelta, que conocía de antemano, luego completar y reemplazar.

Voy a decir que tu idea de usar una matriz existente solo funcionaría si enviases la dirección del elemento específico dentro de la matriz (lo cual es posible) pero tu limitación a lo que la biblioteca de C++ realmente espera cuál es la dirección de la matriz Byte.

+0

El punto era evitar esas operaciones de copiado, ya que son costosas (en términos de tiempo. Esta aplicación procesa más de 30MB/seg de datos) Si lo hacemos como lo describió, entonces, para cada dato que leemos del dispositivo externo, la API de terceros copiará los datos a la matriz pequeña (el tercer argumento en la llamada a ReadFromDevice), y luego, tendremos que hacer otra operación de copia para obtener los datos en la pequeña matriz escrita en nuestra gran matriz. Entonces, por cada 30 MB de datos leídos desde el dispositivo externo, habrá 60 MB de movimiento de datos. Queremos escribir los datos una vez, por razones de rendimiento. – SomethingBetter