Come implementare l’interfaccia di callback dalla DLL non gestita all’app .net?

nel mio prossimo progetto voglio implementare una GUI per codice già esistente in C ++. Il mio piano è quello di avvolgere la parte C ++ in una DLL e di implementare la GUI in C #. Il mio problema è che non so come implementare un callback dalla DLL non gestita nel codice C # manged. Ho già fatto alcuni sviluppi in C # ma l’interfaccia tra codice gestito e non gestito è nuova per me. Qualcuno può darmi qualche suggerimento o consiglio di lettura o un semplice esempio da cui partire? Sfortunatamente non ho trovato nulla di utile.

Non è necessario utilizzare Marshal.GetFunctionPointerForDelegate (), il marshaller P / Invoke lo fa automaticamente. Dovrai dichiarare un delegato sul lato C # la cui firma è compatibile con la dichiarazione del puntatore funzione sul lato C ++. Per esempio:

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

E il codice C ++ corrispondente utilizzato per creare la DLL non gestita:

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

Questo è sufficiente per iniziare. Ci sono un milione di dettagli che possono metterti nei guai, sei obbligato a imbatterti in alcuni di essi. Il modo molto più produttivo per ottenere questo tipo di codice è scrivere un wrapper nel linguaggio C ++ / CLI. Ciò ti consente anche di avvolgere una class C ++, qualcosa che non puoi fare con P / Invoke. Un tutorial decente è disponibile qui.

P / Invoke può gestire il marshalling di un delegato gestito su un puntatore a funzione. Quindi se esponi API che registrano una funzione di richiamata dalla tua DLL e in C # passa un delegato a quella funzione.

C’è un esempio su MSDN di fare ciò con la funzione EnumWindows. In questo articolo fai attenzione a prestare attenzione alla riga al punto 4 che afferma:

Se, tuttavia, la funzione di callback può essere richiamata al termine della chiamata, il chiamante gestito deve adottare misure per garantire che il delegato rimanga non ritirato fino al termine della funzione di callback. Per informazioni dettagliate sulla prevenzione della garbage collection, vedere Interoperare il marshaling con Platform Invoke.

Quello che stai dicendo è che devi assicurarti che il tuo delegato non sia garbage collection fino a quando il codice gestito non è terminato chiamandolo tenendo un riferimento ad esso nel tuo codice, o bloccandolo.

Vedi Marshal.GetFunctionPointerForDelegate , che ti darà un puntatore a funzione per chiamare gestito (cioè codice C #) dal codice non gestito.

Dai un’occhiata a questo, Marshal.GetDelegateForFunctionPointer ?