Como obter o IntPtr do byte [] em c #


127

Eu quero passar um byte[]para um método leva um IntPtrparâmetro em c #, isso é possível e como?


Você poderia fornecer mais detalhes? Por que você quer fazer isso?
11119 Grzenio

2
Você precisa disso se usar a API do DirectShow, por exemplo ... para obter dados do VideoRenderer, você deve usar isso ... e o GCHandlemétodo funciona como um encanto ... também o fixedmétodo. : P :))
Cipi

Você precisa disso para qualquer coisa que esteja transferindo terrabytes de dados e deseja evitar a cópia extra. Use sua imaginação.
precisa saber é o seguinte

Respostas:


93

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ê.


Só para esclarecer, Marshal.Copycom essa sobrecarga precisa de um índice inicial. A chamada deve serMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
mkenyon

melhor obter o IntPtr sem criar nova memória, como a resposta do @ user65157.
Lin

208

De outra maneira

GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();

1
@Cipi Por uma das outras postagens de Eric Lipperts, esta deve ter a palavra-chave Fixed em vez de usar o GC
goodguys_activate

Muito obrigado. Trabalhe bem e é bem fácil. Eu não sou tão habilidoso em GCHandles e Marshal. Alguém pode me dizer quais vantagens o Marshal e o GCHandle e onde usar qual? Obrigado
piggy

1
Alguém poderia fornecer a referência ao post de Eric Lippert?
Cameron

3
@piggy: Eu acho que a desvantagem de Marshal é que você tem que fazer uma cópia de seus dados (que pode levar um longo tempo e desperdício de memória que você pode precisar)
Riki

6
@ makerofthings7 Não acredito que Lippert esteja dizendo para usar 'fixo' em vez do GC [para fixar objetos], acredito que ele esteja dizendo para não usar o GC para fixar um objeto indefinidamente, além de dizer não usar fixo para fazer o o mesmo também.
Cameron

129

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.


16
Eu gosto desta resposta, porque não envolve a alocação de memória extra apenas para acessar os dados
Xcalibur

3
Esta é a melhor resposta se byte grande [] for usado. Uma cópia tem muita sobrecarga
goodguys_activate


13

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 () :)


4
Você pode investigar a derivação do AutoPinner do SafeHandle, pois essa classe leva em consideração as "dicas" de concorrência e segurança, além de incentivar / usar o padrão de IDisposable recomendado.
kkahl 6/12/16

0

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.


-1
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;
}

Esta é uma duplicata mais ineficiente da resposta aceita. Por que você copia byte a byte em vez de todos de uma vez?
BDL

Encontrei o erro na resposta aceita no meu código. Eu chamei a função dll C ++ em c #. parâmetro char * usado em c ++, então usei o método aceito. [DllImport ("MyDll.dll", EntryPoint = "functionName", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] mas estranho, ao usar o método aceito, não é possível obter o IntPtr correto, obtive algum buffer quebrado , algum buffer foi exibido como Unicode. então eu usei esse método
nexdev

-6

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.


14
Você nunca deve usar o Int32 ou o Int64 no lugar de um ponteiro . Se você precisar portar seu código para uma plataforma diferente (32 bits> 64 bits), terá todos os tipos de dores de cabeça.
xxbbcc

5
Não, não há um caso válido em que você possa usar corretamente e com segurança um Int32como ponteiro. Essa foi uma prática ruim feita anos atrás e levou a todos os tipos de problemas de portabilidade. Mesmo um Int64não é seguro, porque já existem arquiteturas de 128 bits e o tamanho do ponteiro aumentará. Os ponteiros devem sempre ser representados como ponteiros.
Xxbbcc

1
Usei-o em projetos .NET CF sem problemas, é claro que você teria problemas se tentar portá-lo para outros sistemas, mas existe um código por aí que não deve ser portado.
Alejandro Mezcua

É verdade que algum código não está planejado para ser portado, mas as coisas podem mudar rapidamente. Mesmo se o seu uso no CF foi justificado (não sei), ainda é um mau conselho para uma pergunta genérica. O único cenário válido para o uso de int/ longpara 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 intum ponteiro no lugar. Retirei meu -1 se você adicionar avisos claros e uma explicação de possíveis problemas à sua resposta.
Xxbbcc 9/11

3
Bem, você está comparando o uso de código de empacotamento muito específico a esta pergunta geral sobre o uso de ponteiros em C #. No C # normal, não há cenário em que isso seja válido. Eu sei que é uma pergunta antiga, mas a rejeitei porque me deparei com ela procurando uma maneira de alocar memória para um IntPtr - outros também verão isso. Eu vejo seus conselhos como muito perigosos, porque as pessoas vão segui-lo pensando que se safaram de resolver um problema facilmente quando tudo o que tinham eram problemas futuros.
Xxbbcc 09/11
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.