2010-01-30 10 views
18

en mi próximo proyecto Quiero implementar una GUI para el código ya existente en C++. Mi plan es envolver la parte de C++ en una DLL e implementar la GUI en C#. Mi problema es que no sé cómo implementar una devolución de llamada desde la DLL no administrada al código C# administrado. Ya he hecho algo de desarrollo en C#, pero la interfaz entre el código administrado y no administrado es nueva para mí. ¿Alguien puede darme algunos consejos o consejos de lectura o un simple ejemplo para empezar? Desafortunadamente no pude encontrar nada útil.¿Cómo implementar la interfaz de devolución de llamada desde la DLL no administrada a la aplicación .net?

Respuesta

42

No necesita usar Marshal.GetFunctionPointerForDelegate(), el Marshalter P/Invoke lo hace automáticamente. Deberá declarar un delegado en el lado C# cuya firma sea compatible con la declaración del puntero a la función en el lado C++. Por ejemplo:

using System; 
using System.Runtime.CompilerServices; 
using System.Runtime.InteropServices; 

class UnManagedInterop { 
    private delegate int Callback(string text); 
    private Callback mInstance; // Ensure it doesn't get garbage collected 

    public UnManagedInterop() { 
    mInstance = new Callback(Handler); 
    SetCallback(mInstance); 
    } 
    public void Test() { 
    TestCallback(); 
    } 

    private int Handler(string text) { 
    // Do something... 
    Console.WriteLine(text); 
    return 42; 
    } 
    [DllImport("cpptemp1.dll")] 
    private static extern void SetCallback(Callback fn); 
    [DllImport("cpptemp1.dll")] 
    private static extern void TestCallback(); 
} 

Y el código correspondiente C++ utilizado para crear el archivo DLL no administrado:

#include "stdafx.h" 

typedef int (__stdcall * Callback)(const char* text); 

Callback Handler = 0; 

extern "C" __declspec(dllexport) 
void __stdcall SetCallback(Callback handler) { 
    Handler = handler; 
} 

extern "C" __declspec(dllexport) 
void __stdcall TestCallback() { 
    int retval = Handler("hello world"); 
} 

Eso es suficiente para empezar con él. Hay un millón de detalles que pueden meterlo en problemas, es probable que se encuentre con algunos de ellos. La forma mucho más productiva de obtener este tipo de código es escribir un contenedor en el lenguaje C++/CLI. Eso también le permite ajustar una clase de C++, algo que no puede hacer con P/Invoke. Un tutorial decente es available here.

+0

Gracias nobugz, lo intentaré pronto. Y espero que no me meta en demasiados problemas ;-) – chrmue

+0

me funciona a la perfección, ¡gracias de nuevo! – chrmue

1

Consulte Marshal.GetFunctionPointerForDelegate, que le proporcionará un puntero a la función para llamar al código administrado (es decir, código C#) desde un código no administrado.

5

P/Invoke puede manejar la clasificación de un delegado administrado a un puntero de función. Entonces, si expone las API que registran una función de devolución de llamada desde su DLL y en C#, pase un delegado a esa función.

Hay un ejemplo en MSDN de hacer esto con la función EnumWindows. En ese artículo, tener cuidado de prestar atención a la línea en el punto 4 que dice:

Si, sin embargo, la función de devolución de llamada puede invocarse después se devuelve la llamada, el persona que llama administrado debe tomar medidas para garantizar que el delegado permanece sin recoger hasta que finaliza la función de devolución de llamada . Para obtener información detallada sobre sobre la prevención de la recolección de basura , consulte Interop Marshaling con Platform Invoke.

Lo que está diciendo es que debe asegurarse de que su delegado no sea basura hasta después de que el código administrado termine de llamarlo manteniendo una referencia en el código o fijándolo.

+0

+1 para crítico: el uso de P/Invoke solo garantiza la validez del puntero durante la llamada; si lo está almacenando en la memoria caché, necesita fijarlo (no se puede usar para esconder una referencia) , soy sospechoso, pero eso no significa mucho :-) –

Cuestiones relacionadas