Eu quero passar um byte[]
para um método leva um IntPtr
parâmetro em c #, isso é possível e como?
GCHandle
método funciona como um encanto ... também o fixed
método. : P :))
Eu quero passar um byte[]
para um método leva um IntPtr
parâmetro em c #, isso é possível e como?
GCHandle
método funciona como um encanto ... também o fixed
método. : P :))
Respostas:
Não tenho certeza sobre como obter um IntPtr em uma matriz, mas você pode copiar os dados para uso com código não gerenciado usando Mashal.Copy:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);
Como alternativa, você pode declarar uma estrutura com uma propriedade e usar Marshal.PtrToStructure, mas isso ainda exigiria a alocação de memória não gerenciada.
Edit: Além disso, como Tyalis apontou, você também pode usar fixo, se código inseguro for uma opção para você.
Marshal.Copy
com essa sobrecarga precisa de um índice inicial. A chamada deve serMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
De outra maneira
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
Isso deve funcionar, mas deve ser usado dentro de um contexto não seguro:
byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
// do you stuff here
}
cuidado, você tem que usar o ponteiro no bloco fixo! O gc pode mover o objeto quando você não estiver mais no bloco fixo.
Você pode usar Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)
para obter um ponteiro de memória para a matriz.
Aqui está uma torção na resposta de @ user65157 (+1 para isso, BTW):
Criei um wrapper IDisposable para o objeto fixado:
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();
}
}
então use-o assim:
using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
UnmanagedIntPtr = ap; // Use the operator to retrieve the IntPtr
//do your stuff
}
Achei que era uma boa maneira de não esquecer de ligar para Free () :)
Marshal.Copy funciona, mas é bastante lento. Mais rápido é copiar os bytes em um loop for. Ainda mais rápido é converter a matriz de bytes em uma matriz ulong, copiar tanto ulong quanto caber na matriz de bytes e copiar os 7 bytes possíveis restantes (a trilha que não está alinhada em 8 bytes). O mais rápido é fixar a matriz de bytes em uma declaração fixa, conforme proposto acima na resposta 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;
}
Em alguns casos, você pode usar um tipo de Int32 (ou Int64) no caso do IntPtr. Se você puder, outra classe útil é o BitConverter. Para o que você deseja, você pode usar o BitConverter.ToInt32, por exemplo.
Int32
como ponteiro. Essa foi uma prática ruim feita anos atrás e levou a todos os tipos de problemas de portabilidade. Mesmo um Int64
não é seguro, porque já existem arquiteturas de 128 bits e o tamanho do ponteiro aumentará. Os ponteiros devem sempre ser representados como ponteiros.
int
/ long
para ponteiros é quando o idioma usado não tem conceito deles (por exemplo, VB6). O C # suporta ponteiros e possui IntPtr
- não há necessidade de usar int
um ponteiro no lugar. Retirei meu -1 se você adicionar avisos claros e uma explicação de possíveis problemas à sua resposta.