2015-02-18 13 views
6

Ho un rawint denominato IntPtr, che punta a una matrice di dati di 10 MB, valori a 16 bit. Ho bisogno di restituire un array ushort gestito da quello. Il seguente codice funziona ma c'è un BlockCopy in più di cui mi piacerebbe sbarazzarmi. Marshal.Copy non supporta l'ushort. Cosa posso fare? (FYI: le rawbits è riempito in da una scheda framegrabber video in memoria non gestita)Copia dall'array IntPtr (16 bit) all'host gestito

public const int width = 2056; 
    public const int height = 2048; 
    public const int depth = 2; 
    public System.IntPtr rawbits; 

public ushort[] bits() 
{ 
    ushort[] output = new ushort[width * height]; 
    short[] temp = new short[width * height]; 
    Marshal.Copy(rawbits, temp, 0, width * height); 
    System.Buffer.BlockCopy(temp, 0, output, 0, width * height * depth); 
    return output; 
} 

I suggerimenti riportati nella seguente domanda non ha aiutato. (errore del compilatore).

C# Marshal.Copy Intptr to 16 bit managed unsigned integer array

[proposito, la breve matrice ha unsigned dati a 16 bit in esso. Marshal.Copy() non rispetta il segno, ed è quello che voglio. Ma preferirei non solo fingere che il corto [] sia un ushort []]

risposta

7

Opzione 1 - chiamano CopyMemory:

[DllImport("kernel32.dll", SetLastError = false)] 
static extern void CopyMemory(IntPtr destination, IntPtr source, UIntPtr length); 

public static void Copy<T>(IntPtr source, T[] destination, int startIndex, int length) 
    where T : struct 
{ 
    var gch = GCHandle.Alloc(destination, GCHandleType.Pinned); 
    try 
    { 
     var targetPtr = Marshal.UnsafeAddrOfPinnedArrayElement(destination, startIndex); 
     var bytesToCopy = Marshal.SizeOf(typeof(T)) * length; 

     CopyMemory(targetPtr, source, (UIntPtr)bytesToCopy); 
    } 
    finally 
    { 
     gch.Free(); 
    } 
} 

Non portatile, ma ha una bella prestazione.


Opzione 2 - unsafe e puntatori:

public static void Copy(IntPtr source, ushort[] destination, int startIndex, int length) 
{ 
    unsafe 
    { 
     var sourcePtr = (ushort*)source; 
     for(int i = startIndex; i < startIndex + length; ++i) 
     { 
      destination[i] = *sourcePtr++; 
     } 
    } 
} 

Richiede unsafe opzione per essere abilitati nel progetto di costruzione Proprietà.


Opzione 3 - la riflessione (solo per divertimento, non utilizzare nella produzione):

Marshal classe utilizza internamente CopyToManaged(IntPtr, object, int, int) metodo per tutti i Copy(IntPtr, <array>, int, int) sovraccarichi (almeno in .NET 4.5). Usando riflessione possiamo chiamare quel metodo direttamente:

private static readonly Action<IntPtr, object, int, int> _copyToManaged = 
    GetCopyToManagedMethod(); 

private static Action<IntPtr, object, int, int> GetCopyToManagedMethod() 
{ 
    var method = typeof(Marshal).GetMethod("CopyToManaged", 
     System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); 
    return (Action<IntPtr, object, int, int>)method.CreateDelegate(
     typeof(Action<IntPtr, object, int, int>), null); 
} 

public static void Copy<T>(IntPtr source, T[] destination, int startIndex, int length) 
    where T : struct 
{ 
    _copyToManaged(source, destination, startIndex, length); 
} 

Dal Marshal interni di classe possono essere cambiati, questo metodo non è affidabile e non deve essere utilizzato, anche se questa implementazione è probabilmente il più vicino ad altri Marshal.Copy() metodo sovraccarichi.

+0

Si dovrebbe usare 'fixed' quando si converte l'array in un puntatore –

+0

' fixed' non può essere usato per creare un metodo generico 'Copia ()' perché 'fixed (T * ptr = array)' non verrebbe compilato. Non esiste un vincolo generico per consentirlo. Anche 'fixed' richiede l'opzione' unssafe', mentre l'uso di 'GCHandle' no. – max

+0

Grandi risposte. Qualche intuizione come la classifica in ordine di prestazione? [Non che io faccia questa conversione nello spazio gestito in modalità normale, ma per il debug è utile deviare il traffico a 25 fps verso l'host, invece di inviarlo alla GPU per l'elaborazione). –

0

Sembra che tu sia bloccato a fare la conversione extra da solo (breve [] per ushort [], quello che fai già), o facendo il mem copia te stesso attraverso la parola chiave non sicura.

C'è la terza opzione: creare una struttura personalizzata.

struct MyMagicalStruct 
{ 
    // todo: set SizeConst correct 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=width*height)] 
    public ushort[] Test123; 
} 

Faresti anche usare Marshal.PtrToStructure<MyMagicalStruct>(yourPtr) ..