2009-12-05 21 views
10

Io utilizzo una vecchia API e devo passare il puntatore di una struct al codice non gestito che viene eseguito in modo asincrono.Bloccare una struttura aggiornabile prima di passare al codice non gestito?

In altre parole, dopo aver passato il puntatore della struct al codice non gestito, il codice non gestito copia il puntatore e restituisce immediatamente. Il codice non gestito può accedere a quella struttura in background, in un'altra discussione. Non ho il controllo sul codice non gestito eseguito in un altro thread né sul thread stesso.

L'istruzione {} fissa non può essere utilizzata per il blocco perché non progettata per il blocco non gestito asincrono.

GCHandle può bloccare solo i riferimenti, pertanto la struttura deve essere racchiusa in un riquadro per utilizzare GCHandle. Ho provato e funziona. Il problema principale è che non è possibile aggiornare la struct dal codice gestito. Per aggiornare una struttura, prima di tutto abbiamo bisogno di unbox, quindi aggiornare, quindi box di nuovo, ma ... oops ... box di nuovo?!? questo significa che il puntatore precedente nella memoria punta ancora alla vecchia struct non aggiornata, e la nuova struct ha un altro puntatore, e questo significa che ho bisogno di passare il nuovo puntatore al codice non gestito ... inapplicabile nel mio Astuccio.

Come posso bloccare una struttura nella memoria senza l'istruzione {} fissa e in modo che possa aggiornarlo dal codice gestito senza cambiare è il puntatore?

Grazie.

Edit:

solo pensato ... c'è un modo per appuntare l'oggetto padre che contiene la struct, e quindi ottenere il puntatore del struct piuttosto che l'oggetto contenitore?

+0

Mi sarei aspettato che GCHandle fosse la soluzione qui. Se tutto fallisce, è possibile allocare la memoria nel codice non gestito, quindi non è soggetto a essere spostato dal GC. – dtb

+1

Buona domanda. Penso di averlo fatto in CLI/C++, dovrò rivedere le mie note di lavoro su di esso. Non sono sicuro se c'è un meccanismo equivalente C#, tuttavia. –

+0

Forse è possibile effettuare la chiamata in un thread in background, utilizzare fixed {} e non lasciare mai il blocco {} fisso (almeno finché il codice non gestito potrebbe accedere alla struttura)? –

risposta

3

È codice non sicuro un'opzione?

// allocate unmanaged memory 
Foo* foo = (Foo*)Marshal.AllocHGlobal(sizeof(Foo)); 

// initialize struct 
foo->bar = 0; 

// invoke unmanaged function which remembers foo 
UnsafeNativeMethods.Bar(foo); 
Console.WriteLine(foo->bar); 

// update struct 
foo->bar = 10; 

// invoke unmanaged function which uses remembered foo 
UnsafeNativeMethods.Qux(); 
Console.WriteLine(foo->bar); 

// free unmanaged memory 
Marshal.FreeHGlobal((IntPtr)foo); 

Questo compila e non genera un'eccezione, ma non ho una funzione non gestita per verificare se funziona.

Da MSDN:

Quando AllocHGlobal chiama LocalAlloc, passa una bandiera LMEM_FIXED, che causa la memoria assegnata ad essere bloccata in posizione. Inoltre, la memoria allocata non è riempita a zero.

+0

Grazie, funziona come un fascino! in realtà, non ho usato AllocHGlobal e FreeHGlobal, ho appena preso l'IntPtr che GCHandle mi ha dato e aggiornato la struttura tramite codice non sicuro. Perdonami se ancora non segnalo come risposta, perché voglio aspettare altre possibili risposte, forse ci saranno soluzioni migliori senza codice non sicuro. – DxCK

1

Invece di bloccare, è necessario utilizzare Marshal.StructureToPtr e Marshal.PtrToStructure per eseguire il marshalling della struttura in memoria che è utilizzabile nel codice nativo.

+0

Sembra che voglia aggiornare la struct da C# dopo che è stato passato al codice non gestito ... – dtb

+0

"StructureToPtr copia il contenuto della struttura nel blocco di memoria pre-allocato ..." Questo non è proprio ciò che l'OP ha chiesto per. Vuole consentire al codice nativo di manipolare direttamente la memoria degli oggetti. Presumo che questo sia dovuto a motivi di prestazioni ... Forse mi sbaglio. –

+0

Non puoi. Devi copiarlo in un blocco di memoria assegnato dal marshal, aggiornarlo in C++, quindi copiarlo nello stack. Non c'è un modo diretto per farlo altrimenti. Se non vuoi farlo, l'opzione migliore è usare C++/CLI. –

0

Struct esempio:

[StructLayout(LayoutKind.Sequential)] 
public struct OVERLAPPED_STRUCT 
{ 
    public IntPtr InternalLow; 
    public IntPtr InternalHigh; 
    public Int32 OffsetLow; 
    public Int32 OffsetHigh; 
    public IntPtr EventHandle; 
} 

Come al pin alla struct e usarlo:

OVERLAPPED_STRUCT over_lapped = new OVERLAPPED_STRUCT(); 
// edit struct in managed code 
over_lapped.OffsetLow = 100; 
IntPtr pinned_overlap_struct = Marshal.AllocHGlobal(Marshal.SizeOf(over_lapped)); 
Marshal.StructureToPtr(over_lapped, pinned_overlap_struct, true); 

// Pass pinned_overlap_struct to your unmanaged code 
// pinned_overlap_struct changes ... 

// Get resulting new struct 
OVERLAPPED_STRUCT nat_ov = (OVERLAPPED_STRUCT)Marshal.PtrToStructure(pinned_overlap_struct, typeof(OVERLAPPED_STRUCT)); 
// See what new value is 
int offset_low = nat_ov.OffsetLow; 
// Clean up 
Marshal.FreeHGlobal(pinned_overlap_struct); 
6

Utilizzo della memoria appuntato in questo caso non è una buona idea, dato che la memoria per la struct deve essere valido per un lungo periodo di tempo. GCHandle.Alloc() inserirà la struttura nella casella e la memorizzerà nell'heap. Con il suo essere bloccato, sarà un onere a lungo termine per il netturbino in quanto ha bisogno di trovare costantemente un modo per aggirare la roccia in mezzo alla strada.

La soluzione semplice è allocare memoria per la struttura nella memoria non gestita. Utilizzare Marshal.SizeOf() per ottenere la dimensione della struttura e Marshal.AllocCoTaskMem() per allocare la memoria. Questo ti porta il puntatore che devi passare al codice non gestito. Inizializza la memoria con Marshal.StructureToPtr(). E leggere gli aggiornamenti della struttura scritti dal codice non gestito con PtrToStructure().

Se lo fai frequentemente, copi costantemente la struttura. Potrebbe essere costoso, a seconda delle dimensioni della struttura. Per evitare ciò, utilizzare un puntatore non sicuro per accedere direttamente alla memoria non gestita. Alcuni sintassi di base:

using System; 
using System.Runtime.InteropServices; 

class Program { 
    unsafe static void Main(string[] args) { 
    int len = Marshal.SizeOf(typeof(Test)); 
    IntPtr mem = Marshal.AllocCoTaskMem(len); 
    Test* ptr = (Test*)mem; 
    ptr->member1 = 42; 
    // call method 
    //.. 
    int value = ptr->member1; 
    Marshal.FreeCoTaskMem(mem); 
    } 
    public struct Test { 
    public int member1; 
    } 
} 
+0

Qual è la differenza tra l'utilizzo di tutti questi metodi: Marshal.AllocCoTaskMem e Marshal.FreeCoTaskMem VS Marshal.AllocHGlobal e Marshal.FreeHGlobal, sizeof VS Marshal.SizeOf e quali sono i metodi corretti da utilizzare nel mio caso e perché? tnx. – DxCK

+0

Non riesco ad adattare la risposta in una casella di commento.Perché non inizi una nuova discussione con quella domanda? –

+0

http://stackoverflow.com/questions/1887288/marshal-allochglobal-vs-marshal-alloccotaskmem-marshal-sizeof-vs-sizeof – DxCK

0

ne dite di avere lo struct includono un'interfaccia ActOnMe() e il metodo di qualcosa di simile a:

 
delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); 
interface IActOnMe<TT> {ActOnMe<T>(ActByRef<TT,T> proc, ref T param);} 
struct SuperThing : IActOnMe<SuperThing> 
{ 
    int this; 
    int that; 
    ... 
    void ActOnMe<T>(ActByRef<SuperThing,T>, ref T param) 
    { 
    proc(ref this, ref param); 
    } 
} 

Perché il delegato prende un parametro generico per riferimento, dovrebbe essere possibile nella maggior parte dei casi a evitare il sovraccarico di creazione di chiusure passando un delegato a un metodo statico insieme a un riferimento a una struttura per trasportare dati da o verso tale metodo. Inoltre, esprimono un esempio già in confezione di SuperThing-IActOnMe<SuperThing> e chiamando ActOnMe<T> su di esso esporrà i campi di tale istanza boxed per l'aggiornamento, invece di creare un'altra copia di loro come accadrebbe con un typecast alla struct.