2010-11-10 16 views
5

come Hans Passantwishes ecco lo scenario del mio. Ho un'applicazione in modalità mista in cui il codice nativo fa tutto il lavoro duro rispettando le prestazioni e il codice gestito è responsabile solo della GUI. Anche gli utenti parteciperanno scrivendo il loro codice C# proprietario. Ho C++ per le classi native, C# per la GUI e il codice utente e C++/Cli per le classi wrapper in mezzo. Tra tutte le mie classi C++ ce n'è una che fa% 90 dei calcoli e ogni volta viene creato un parametro diverso. Chiamiamolo NativeClass. Ci sono apprx. 2000 istanze di questo NativeClass e io dobbiamo trovare l'istanza giusta relativa ad alcuni parametri prima che faccia il calcolo. Quindi ho ideato una hash_map, con i parametri come codice hash, per questo scopo. Quando ottengo un parametro, cerco l'istanza giusta in hash_map, la trovo e richiamo alcuni dei suoi metodi.
Quando gli utenti eseguono il calcolo dei calcoli scrivendo il codice C# e questa classe esegue questi codici mediante richiamate. Questo è banale, ma a volte ho bisogno di alcune informazioni sulle classi .Net create dagli utenti. Quindi ho bisogno di collegare quella specifica ManagedClass a NativeClass in qualche modo. La mia prima soluzione è l'utilizzo di GChandle.Alloc() e il trasferimento dell'indirizzo dei manici. Ma ci sono alcuni concerns su GC che non farà il suo lavoro correttamente. Hans ha raccomandato Marshal.AllocCoTaskMem() e Marshal.StructureToPtr() per allocare oggetti gestiti nella memoria non gestita, tuttavia ritengo che questo sia valido per classi di tipi di valore o strutture. Che ne dici di lezioni di ref? Come posso passare un riferimento a NativeClass mentre impedisco loro di essere raccolti in GC e di far funzionare correttamente GC allo stesso tempo?GCHandle, maresciallo, memoria gestita e non gestita: pin o not to pin

Ecco alcuni esempi di codice:

class NativeClass 
{ 
private: 
    int AddressOfManagedHandle; 
public: 
    static NativeClass * GetNativeClassFromHashMap(int SomeParameter) 
    { 
// return NativeClass associated with SomeParameter from NativeClassHashMap; 
    } 
    NativeClass(int addr, int SomeParameter) : AddressOfManagedHandle(addr) 
    { 

    } 
    int GetAddress(){return AddressOfManagedHandle;} 
void DoCalculation(){ 
// CALCULATIONS 
} 
}; 


public ref class ManagedClass : MarshalByRefObject 
{ 
private: 
    NativeClass* _nc; 
//GCHandle handle; 
    void FreeManagedClass() 
    { 
     Marshal::FreeHGlobal(IntPtr(_nc->GetAddress())); 
//if(handle.IsAllocated) 
//handle.Free(); 
     delete _nc; 
    } 
public: 
    ManagedClass() 
    { 
     IntPtr addr = (Marshal::AllocHGlobal(Marshal::Sizeof(this))); // Error 
     Marshal::StructureToPtr(this,addr,true); 
//handle = GCHandle.Alloc(this); 
//IntPtr addr = handle.ToIntPtr(); 
     _nc = new NativeClass(addr.ToInt32()); 
    } 
    ~ManagedClass() {FreeManagedClass();} 
    !ManagedClass() {FreeManagedClass();} 
    int GetAddress() {return _nc->GetAddress();}; 
    static ManagedClass^ GetManagedClass(int SomeParameter) 
    { 
int addr = NativeClass::GetNativeClassFromHashMap(SomeParameter)->GetAddress(); 
//Object^obj = GCHandle::FromIntPtr(IntPtr(addr)).Target; 
Object^ obj = Marshal::PtrToStructure(IntPtr(addr), ManagedClass::typeid); 
    return dynamic_cast<ManagedClass^>(obj); 

    } 
}; 

Mi dispiace che sia toooooo lungo e ancora non è chiaro.

+1

È necessario utilizzare IntPtr anziché int per archiviare i puntatori nativi. Altrimenti il ​​tuo codice potrebbe bloccarsi su Windows 64 bit. – Elmue

+0

Non si dovrebbe liberare la memoria in un'altra classe. Scrivi un distruttore (finalizzatore) ~ NativeClass() che chiama FreeHglobal(). – Elmue

risposta

3

ho trascorso un po 'di tempo alle prese con un problema simile e questo è il contorno della soluzione che ha funzionato per me ....

  1. Conservare la maniglia per la classe gestita come un void *
  2. Conservare un puntatore alla classe non gestita nella classe gestita (come hai fatto)
  3. Usa pianura vecchio new e delete piuttosto che qualsiasi cosa, come AllocHGlobal
  4. Trasformare il GCHandle tra voi d * e riferimento oggetto gestito
  5. Non preoccuparti derivanti da MarshalByRefObject - non è necessario come si sta facendo il proprio smistamento
  6. Ricordate codifica difensiva quando liberare la classe gestita

ho preso il tuo codice sopra e violato per ottenere:

class NativeClass 
{ 
private: 
    void * managedHandle; 
public: 
    static NativeClass * GetNativeClassFromHashMap(int SomeParameter) 
    { 
     // return NativeClass associated with SomeParameter from NativeClassHashMap; 
    } 
    NativeClass(void *handle, int SomeParameter) 
     : managedHandle(handle) 
    { 
    } 
    void * ManagedHandle() 
    { 
     return managedHandle; 
    } 
    void DoCalculation() 
    { 
     // CALCULATIONS 
    } 
}; 

public ref class ManagedClass 
{ 
private: 
    NativeClass* _nc; 
    void FreeManagedClass() 
    { 
     if (_nc) 
     { 
      // Free the handle to the managed object 
      static_cast<GCHandle>(IntPtr(_nc->ManagedHandle)).Free(); 
      // Delete the native object 
      delete _nc; 
      _nc = 0; 
     } 
    } 
public: 
    ManagedClass() 
    { 
     // Allocate GCHandle of type 'Normal' (see doco for Normal, Weak, Pinned) 
     GCHandle gch = GCHandle::Alloc(this, GCHandleType::Normal); 
     // Convert to void* 
     void *handle = static_cast<IntPtr>(gch).ToPointer(); 
     // Initialise native object, storing handle to native object as void* 
     _nc = new NativeClass(handle); 
    } 
    ~ManagedClass() {FreeManagedClass();} 
    !ManagedClass() {FreeManagedClass();} 
    static ManagedClass^ GetManagedClass(int SomeParameter) 
    { 
     // Native class is retrieved from hash map 
     NativeClass *nc = NativeClass::GetNativeClassFromHashMap(SomeParameter); 
     // Extract GCHandle from handle stored in native class 
     // This is the reverse of the process used in the ManagedClass constructor 
     GCHandle gch = static_cast<GCHandle>(IntPtr(nc->ManagedHandle())); 
     // Cast the target of the GCHandle to the managed object 
     return dynamic_cast<ManagedClass^>(gch.Target); 
    } 
}; 

Questo dovrebbe metterti sulla giusta strada.

+2

Il vantaggio di questo metodo rispetto all'utilizzo di gcroot è che NativeClass può trovarsi in una DLL C++ separata che viene utilizzata dal wrapper C++/CLI. Questo era quello di cui avevo bisogno. – mcdave

+0

Sì, il mio codice, che sto usando proprio ora, è proprio come il tuo. Le cose con la classe Marshal non funzionano. Ma ho fatto la domanda solo per essere sicuro di quello che sta succedendo. Solo le differenze sono non memorizzare handle in ManagedClass in ManagedClass e NativeClass ha l'indirizzo di handle come puntatore non come numero intero. Lasciami fare 3 domande. da quanto tempo è in esecuzione questo codice? Hai qualche problema legato alla memoria? Di cosa parla la tua domanda? –

+0

Ha lavorato per ~ 3 anni; Nessun problema di memoria a meno che non si dimentichi di liberare l'handle gestito, nel qual caso gli oggetti gestiti non vengono mai raccolti; L'applicazione è di tipo tecnico che, come la tua, richiede l'esecuzione di codice nativo pur avendo l'interfaccia utente in .NET. – mcdave

0

Hmmm.

GCHandle è una struttura.

In alcuni casi il passaggio di un GCHandle ref non bloccato farà ciò che si desidera.

+0

Beh, pensavo di si. GCHandle.Alloc con GCHandleTypes.Normal sembra ok per me. Questo è ciò che MSDN dice "Questo tipo di handle rappresenta un handle opaco, il che significa che non è possibile risolvere l'indirizzo dell'oggetto aggiunto attraverso l'handle. È possibile utilizzare questo tipo per tenere traccia di un oggetto e impedirne la raccolta da parte del garbage collector. Questo membro di enumerazione è utile quando un client non gestito contiene l'unico riferimento, che non è rilevabile dal garbage collector, a un oggetto gestito. Su GCHandle.Normal. –

Problemi correlati