2010-11-10 7 views
5

Como Hans Passantwishes aquí está el escenario mío. Tengo una aplicación de modo mixto en la que el código nativo hace todo el trabajo mientras respeta el rendimiento y el código administrado es responsable únicamente de la GUI. También los usuarios participarán escribiendo su código de propiedad de C#. Tengo C++ para clases nativas, C# para GUI y código de usuario y C++/Cli para clases de contenedor entre. Entre todas mis clases de C++ hay una que hace% 90 de los cálculos y se crea un parámetro diferente cada vez. Llamémoslo NativeClass. Hay un aprx. 2000 instancias de este NativeClass y tengo que encontrar la instancia correcta relacionada con algún parámetro antes de hacer el cálculo. Así que ideé un hash_map, con parámetros que son el código hash, para este propósito. Cuando obtengo un parámetro, busco la instancia correcta en hash_map, la encuentro y llamo a algunos de sus métodos.
Cuando los usuarios contribuyen a los cálculos escribiendo código C# y esta clase ejecuta estos códigos mediante devoluciones de llamada. Esto es trivial, pero a veces necesito cierta información sobre las clases .Net que los usuarios construyeron. Así que tengo que adjuntar ese ManagedClass específico a NativeClass de alguna manera. Mi primera solución es usar GChandle.Alloc() y transferir la dirección de los controladores. Pero hay algunos concerns sobre GC que no harán su trabajo correctamente. Hans recomendó Marshal.AllocCoTaskMem() y Marshal.StructureToPtr() para asignar objetos administrados en la memoria no administrada, sin embargo, creo que esto es válido para las clases de clase de valor o las estructuras. ¿Qué hay de las clases de ref? ¿Cómo puedo pasar una referencia a NativeClass mientras evito que se recolecten GC y hacer que GC funcione correctamente al mismo tiempo?GCHandle, Marshal, memoria administrada y no administrada: Para fijar o No para marcar

Aquí hay un código de ejemplo:

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); 

    } 
}; 

lo siento que no es demasiadooooooooooooo larga y todavía clara.

+1

Debe utilizar IntPtr en lugar de int para almacenar punteros nativos. De lo contrario, su código puede bloquearse en Windows de 64 bits. – Elmue

+0

No debe liberar la memoria en otra clase. Escriba un destructor (finalizador) ~ NativeClass() que llama a FreeHglobal(). – Elmue

Respuesta

3

pasé bastante tiempo luchando con un problema similar y este es el esquema de la solución que funcionó para mí ....

  1. tienda el mango a la clase administrada como una tienda void *
  2. un puntero a la clase no gestionado en la clase administrada (como lo han hecho)
  3. uso viejo y simple new y delete más que cualquier cosa, como AllocHGlobal
  4. Transformación GCHandle entre el Voi dy referencia de objeto gestionado
  5. * No se preocupe por que deriva de MarshalByRefObject - que no lo necesita como lo están haciendo su propio cálculo de referencias
  6. Recuerde codificación defensiva cuando liberando la clase administrada

Tomé su código anterior y hackeado en él para obtener:

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

Esto debería ponerlo en el camino correcto.

+2

La ventaja de este método sobre el uso de gcroot es que NativeClass puede estar en una DLL C++ separada que es consumida por un contenedor C++/CLI. Esto era lo que necesitaba. – mcdave

+0

Sí, mi propio código, estoy usando en este momento, es como el tuyo. Las cosas con la clase Marshal no funcionan. Pero hice la pregunta solo para estar seguro de lo que está pasando. Solo las diferencias son que usted no almacena manejar a ManagedClass en ManagedClass y NativeClass tiene la dirección de manejo como un puntero no como un entero. Déjame hacer 3 preguntas. ¿Cuánto tiempo lleva funcionando este código? ¿Tiene algún problema relacionado con la memoria? ¿De qué trata su aplicación? –

+0

Estado trabajando desde hace ~ 3 años; No hay problemas de memoria a menos que olvide liberar el identificador administrado, en cuyo caso los objetos administrados nunca se recolectan como basura. La aplicación es una aplicación técnica que, como la tuya, requiere el rendimiento del código nativo al tener la interfaz de usuario en .NET. – mcdave

0

Hmmm.

GCHandle es una estructura.

En algunos casos, pasar un GCH y una referencia sin fijar hará lo que quiera.

+0

Bueno, eso creía. GCHandle.Alloc con GCHandleTypes.Normal me parece bien. Eso es lo que dice MSDN. Este tipo de identificador representa un identificador opaco, lo que significa que no puede resolver la dirección del objeto inmovilizado a través del identificador. Puede utilizar este tipo para rastrear un objeto e impedir que el recolector de elementos no utilizados lo recopile. Este miembro de enumeración es útil cuando un cliente no administrado contiene la única referencia, que es indetectable desde el recolector de elementos no utilizados, a un objeto gestionado "en GCHandle.Normal. –

Cuestiones relacionadas