Quiero pasar byte[]a un método toma un IntPtrparámetro en C #, ¿es eso posible y cómo?
GCHandlemétodo funciona de maravilla ... también el fixedmétodo. : P :))
Quiero pasar byte[]a un método toma un IntPtrparámetro en C #, ¿es eso posible y cómo?
GCHandlemétodo funciona de maravilla ... también el fixedmétodo. : P :))
Respuestas:
No estoy seguro de obtener un IntPtr en una matriz, pero puede copiar los datos para usarlos con código no administrado utilizando Mashal.Copy:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);
Alternativamente, podría declarar una estructura con una propiedad y luego usar Marshal.PtrToStructure, pero eso aún requeriría asignar memoria no administrada.
Editar: Además, como señaló Tyalis, también puede usar el código fijo si no es seguro para usted.
Marshal.Copycon esa sobrecarga se necesita un índice de inicio. La llamada debería serMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
De otra manera,
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
Esto debería funcionar pero debe usarse dentro de un contexto inseguro:
byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
// do you stuff here
}
¡cuidado, tienes que usar el puntero en el bloque fijo! El gc puede mover el objeto una vez que ya no esté en el bloque fijo.
Puede usar Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)para obtener un puntero de memoria a la matriz.
Aquí hay un giro en la respuesta de @ user65157 (+1 para eso, por cierto):
Creé un contenedor IDisposable para el objeto anclado:
class AutoPinner : IDisposable
{
GCHandle _pinnedArray;
public AutoPinner(Object obj)
{
_pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
}
public static implicit operator IntPtr(AutoPinner ap)
{
return ap._pinnedArray.AddrOfPinnedObject();
}
public void Dispose()
{
_pinnedArray.Free();
}
}
luego úsalo así:
using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
UnmanagedIntPtr = ap; // Use the operator to retrieve the IntPtr
//do your stuff
}
Encontré que esta es una buena manera de no olvidar llamar a Free () :)
Marshal.Copy funciona pero es bastante lento. Más rápido es copiar los bytes en un bucle for. Incluso más rápido es convertir la matriz de bytes en una matriz ulong, copiar tantos ulong como quepan en la matriz de bytes, luego copiar los 7 bytes restantes posibles (el camino que no está alineado con 8 bytes). Lo más rápido es anclar la matriz de bytes en una declaración fija como se propuso anteriormente en la respuesta de Tyalis.
IntPtr GetIntPtr(Byte[] byteBuf)
{
IntPtr ptr = Marshal.AllocHGlobal(byteBuf.Length);
for (int i = 0; i < byteBuf.Length; i++)
{
Marshal.WriteByte(ptr, i, byteBuf[i]);
}
return ptr;
}
En algunos casos, puede usar un tipo Int32 (o Int64) en el caso de IntPtr. Si puede, otra clase útil es BitConverter. Para lo que quieras, puedes usar BitConverter.ToInt32 por ejemplo.
Int32un puntero. Esta fue una mala práctica realizada hace años y conduce a todo tipo de problemas de portabilidad. Incluso un Int64no es seguro porque ya hay arquitecturas de 128 bits y el tamaño del puntero aumentará. Los punteros solo deben representarse como punteros.
int/ longpara punteros es cuando el lenguaje utilizado no tiene un concepto de ellos (por ejemplo, VB6). C # admite punteros y tiene IntPtr: no hay necesidad de usar un intpuntero en lugar de un puntero. Eliminaré mi -1 si agrega advertencias claras y una explicación de posibles problemas a su respuesta.