¿Es seguro este uso de gcroot?

Necesito usar una API no administrada de C++/CLI. Esta API almacena un puntero vacío a datos de usuario arbitrarios y algunas devoluciones de llamada. Luego, eventualmente llama a esas devoluciones de llamada, pasando los datos del usuario como void*.

Hasta ahora, tenía una clase nativa que pasaba su puntero "este" como los datos del usuario y usaba ese puntero para que la API volviera a llamar a esta clase, es decir:

static void __stdcall Callback(void* userData) {
    ((MyType*)userData)->Method();
}

class MyType {
public:
    MyType() { RegisterWithApi((void*)this, Callback); }
    void Method();
};

Estoy tratando de traducir esto usando una clase administrada. Descubrí que el tipo gcroot se puede usar para almacenar de manera segura una referencia administrada en código nativo, así es como lo estoy haciendo ahora:

// This is called by the native API
static void __stdcall Callback(void* userData) {
    // Cast back to gcroot and call into managed code
    (*(gcroot<MyType^>*)userData)->Method();
}

ref class MyType {
    gcroot<MyType^>* m_self;
public:
    MyType() { 
        m_self = new gcroot<MyType^>;
        RegisterWithApi((void*)m_self, Callback);
    }
    ~MyType() { delete m_self; }
    // Method we want called by the native API
    void Method();
}

Si bien esto parece correcto para el compilador C++/CLI, no estoy completamente seguro. Por lo que entiendo, gcroot de alguna manera realiza un seguimiento de su referencia administrada a medida que el GC lo mueve. ¿Se las arreglará para hacer esto mientras se almacena como un vacío * por código no administrado? ¿Es seguro este código?

Gracias.

preguntado el 08 de marzo de 13 a las 21:03

Favor de Marshal::GetFunctionPointerForDelegate(), un ejemplo es aquí -

2 Respuestas

Esto es lo que terminé haciendo y funciona perfectamente. El propósito de gcroot es almacenar una referencia administrada en el montón nativo, que es precisamente lo que estoy haciendo aquí.

Respondido 04 Abr '13, 03:04

¡No! Es exactamente al revés. gcroot es una plantilla de clase nativa. Lo usa para almacenar un identificador en la memoria administrada en un tipo nativo que se compila con soporte clr. Por lo general, lo usará para desviar llamadas a funciones miembro de un objeto nativo a un objeto administrado almacenado en un miembro de tipo gcroot.

EDITAR: Ayer estaba en el móvil, donde escribir ejemplos de código es un poco incómodo... El uso previsto y típico de gcroot<T^> está en algún lugar a lo largo de estas líneas:

// ICallback.h
struct ICallback {
    virtual void Invoke() = 0;
    virtual void Release() = 0;
    protected:
        ~ICallback() {}
};

Eso es lo que ven e incluyen sus aplicaciones o bibliotecas nativas. Luego, tiene un componente mixto compilado con soporte CLR, que implementa ICallback y almacena un identificador para algún objeto administrado en un gcroot<ManagedType^>:

// Callback.cpp (this translation unit must be compiled with /clr)
// I did not compile and test, but you get the point...
template<class T^> class Callback : public ICallback {
    gcroot<T^> m_Managed;

    virtual void Invoke()
    {
       m_Managed->Invoke();
    }

    virtual void Release()
    {
        delete this;
    }
public:
    Callback(T^ p_Managed) : m_Managed(p_Managed) {}
};

__declspec( dllexport ) ICallback* CreateCallback()
{
    auto t_Managed = gcnew SomeManagedType();
    return new Callback<System::Action^>(
        gcnew System::Action(t_Managed, &SomeManagedType::Method)); 
}

Sus aplicaciones nativas llaman CreateCallback, recibe una instancia de ICallback que cuando Invoke-d llama a un método de tipo administrado, retenido en gcroot<System::Action^>...

respondido 09 mar '13, 11:03

Entonces, ¿el problema es que la API nativa no está compilada con soporte clr? Porque de lo contrario lo que describes es exactamente lo que estoy haciendo. - Asik

La clase nativa que usa gcroot debe compilarse con soporte clr. Lo que describí es exactamente lo contrario de lo que ha mostrado en su pregunta: almacena un puntero gcroot nativo en un objeto administrado. - Pablo Michalik

Bueno, la función donde se usa gcroot ("Callback") is compilado con soporte CLR. El gcroot se almacena en el montón no administrado que aparentemente está permitido. ¿Qué diferencia haría que una clase administrada mantenga un puntero hacia ella? - Asik

Bueno, ese no es el uso previsto de gcroot. Todo el punto de tener un gcroot<T^> sin que importe T es un tipo administrado es usar un contenedor RAII seguro para los identificadores de la memoria administrada. Usando gcroot<T^> asignado en la tienda libre es similar a std::shared_ptr<T>* p = new std::shared_ptr<T> - sin sentido Editaré mi respuesta para demostrar... - Pablo Michalik

¿Cómo es inútil si resuelve mi problema? Quiero pasar una instancia de una clase administrada como nula* a una API sobre la que no tengo control. Crear un gcroot en el montón nativo y pasarlo como void* parece funcionar, ya que cuando me devuelven la llamada con este puntero, puedo volver a gcroot y reenviarlo a la clase administrada. No estoy preguntando cuál es el uso típico, estoy preguntando si esto funcionará correctamente. - Asik

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.