2012-03-16 13 views
19

Quiero obtener datos de un puntero IntPtr en una matriz de bytes. Puedo usar el siguiente código para hacerlo:¿Se puede convertir IntPtr en una matriz de bytes sin hacer un Marshal.Copy?

IntPtr intPtr = GetBuff(); 
byte[] b = new byte[length]; 
Marshal.Copy(intPtr, b, 0, length); 

Pero el código anterior obliga a una operación de copia de IntPtr en la matriz de bytes. No es una buena solución cuando los datos en cuestión son grandes.

¿Hay alguna forma de convertir un IntPtr en una matriz de bytes? Por ejemplo, sería el siguiente trabajo:

byte[] b = (byte[])intPtr

Esto eliminaría la necesidad de que la operación de copia.

También: ¿cómo podemos determinar la longitud de los datos apuntados por IntPtr?

+1

¡Debería copiar los datos porque el 'IntPtr' apunta a * memoria no administrada *! – Yahia

+3

Gracias por su rápida respuesta, pero ese IntPtr fue devuelto desde la función de exportación C++, por lo que puedo administrarlo y eliminarlo en cualquier lugar que desee. No quiero copiar los datos porque los datos son búfer de video, por lo que es muy grande. La copia tendrá un mal rendimiento. ¡Gracias! – TTGroup

+1

Eso realmente no es razón para ir por prácticas inseguras ... realmente debe copiar el contenido; hay varios métodos seguros para hacerlo (algunos más rápidos que otros) ... – Yahia

Respuesta

17

Como otros han mencionado, no hay manera que puede almacenar los datos en un administradobyte[] sin copiar (con la estructura actual que nos ha facilitado *). Sin embargo, si realmente no necesita en un búfer administrado, puede usar las operaciones unsafe para trabajar directamente con la memoria no administrada. Realmente depende de lo que necesites hacer con eso.

Todos byte[] y otros tipos de referencia son gestionados por el recopilador de basura CLR, y esto es lo que es responsable de la asignación de memoria y la desasignación cuando ya no se utiliza. La memoria apuntada por el retorno de GetBuffer es un bloque de memoria no administrada asignada por el código C++ y (detalles de implementación/diseño de memoria aparte) es esencialmente completamente independiente de la memoria administrada por GC. Por lo tanto, si desea utilizar un tipo de CLR gestionado por GC (byte[]) para contener todos los datos actualmente almacenados en su memoria no administrada a la que apunta su IntPtr, debe moverse (copiarse) a la memoria que el GC conoce. Esto se puede hacer por Marshal.Copy o por un método personalizado usando el código unsafe o pinvoke o lo que sea.

Sin embargo, depende de lo que quiera hacer con él. Has mencionado que son datos de video. Si desea aplicar alguna transformación o filtro a los datos, probablemente pueda hacerlo directamente en el búfer no administrado. Si desea guardar el búfer en el disco, probablemente pueda hacerlo directamente en el búfer no administrado.

Sobre el tema de la longitud, no hay forma de saber la longitud de un búfer de memoria no administrado a menos que la función que asignó el búfer también le indique cuál es la longitud. Esto se puede hacer de muchas maneras, como lo han mencionado los comentaristas (primer campo de la estructura, apartado del método).

* Finalmente, si tiene control del código C++, podría ser posible modificarlo para que no sea responsable de asignar el búfer al que escribe los datos, y en su lugar se le proporciona un puntero a un búfer preasignado. A continuación, puede crear un administradobyte[] en C#, preasignado al tamaño requerido por su código C++, y utilizar el tipo GCHandle para fijarlo y proporcionar el puntero a su código C++.

+0

Gracias por su explicación detallada, he usado código inseguro para hacerlo :) – TTGroup

2

No se puede hacer que una matriz administrada ocupe la memoria no administrada. Puede copiar los datos no administrados un fragmento a la vez y procesar cada fragmento, o crear una clase UnmanagedArray que tome un IntPtr y proporcione un indexador que aún use Marshal.Copy para acceder a los datos.

Como @Vinod ha señalado, puede hacerlo con el código unsafe. Esto le permitirá acceder a la memoria directamente, utilizando punteros tipo C. Sin embargo, tendrá que ordenar los datos en la memoria administrada antes de llamar a cualquier método .NET inseguro, por lo que está prácticamente limitado a su propio código tipo C. No creo que debas molestarte con esto en absoluto, solo escribe el código en C++.

+0

¿Ni siquiera con cosas "inseguras"? –

+0

Ah, cosas inseguras. Sí, puedes hacer eso con cosas inseguras. ¿De verdad quieres cosas inseguras? – zmbq

+0

No estoy seguro, no es mi pregunta :) Pero sería interesante ver cómo esto * podría * hacerse, incluso si hay grandes banderas de advertencia. –

8

Prueba esto:

byte* b = (byte*)intPtr; 

Requiere unsafe (en la función de firma, bloque, o la bandera del compilador /unsafe).

+5

Debe mencionar que esto requiere la palabra clave 'inseguro' en la firma de la función o dentro de un par de llaves:' usafe {...} ' –

+0

Gracias, acabo de probarlo. Pero el compilador arrojó el error al construir "Los punteros y los almacenamientos intermedios de tamaño fijo solo pueden usarse en un contexto inseguro". No entiendo este error, por favor explícamelo. ¡Gracias! – TTGroup

+2

Su proyecto debe configurarse para permitir código no seguro, el código indicado en la respuesta debe estar envuelto en inseguro (como dijo @MikeBantegui), y lo más importante, esto le proporciona un puntero no administrado a la memoria no administrada, no a un CLR byte [] referencia – jeffora

1

Consulte esta página Code Project para encontrar una solución al trabajo con matrices no administradas.

Cuestiones relacionadas