web-dev-qa-db-fra.com

Comment implémenter une interface de rappel depuis unmanaged DLL à l'application .net?

dans mon prochain projet, je souhaite implémenter une interface graphique pour du code déjà existant en C++. Mon plan consiste à envelopper la partie C++ dans un DLL et à implémenter l'interface graphique en C #. Mon problème est que je ne sais pas comment implémenter un rappel du DLL non géré dans le code C # mangé. J'ai déjà réalisé des développements en C #, mais l'interface entre le code géré et le code non géré est nouvelle pour moi. Quelqu'un peut-il me donner des astuces ou des conseils de lecture ou un exemple simple pour commencer? Malheureusement, je n'ai rien trouvé d'utile.

23
chrmue

Vous n'avez pas besoin d'utiliser Marshal.GetFunctionPointerForDelegate (), le marshaller P/Invoke le fait automatiquement. Vous devez déclarer un délégué du côté C # dont la signature est compatible avec la déclaration du pointeur de la fonction du côté C++. Par exemple:

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

Et le code C++ correspondant utilisé pour créer la DLL non gérée:

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

C'est assez pour vous aider à commencer. Il y a un million de détails qui peuvent vous causer des ennuis, vous en rencontrerez certainement certains. Le moyen le plus productif d'obtenir ce type de code consiste à écrire un wrapper en langage C++/CLI. Cela vous permet également d’envelopper une classe C++, chose que vous ne pouvez pas faire avec P/Invoke. Un tutoriel décent est disponible ici.

49
Hans Passant

P/Invoke peut gérer le marshaling d'un délégué géré à un pointeur de fonction. Donc, si vous exposez des API qui enregistrent une fonction de rappel de votre DLL et passez en C # un délégué à cette fonction.

Il existe un exemple sur MSDN avec la fonction EnumWindows. Dans cet article, faites attention à la ligne du point 4 qui dit:

Si, toutefois, la fonction de rappel peut Être appelée après le retour de l'appel, l'appelant géré Doit procéder comme suit: La fonction de rappel se termine. Pour des informations détaillées sur Sur la prévention de la collecte de déchets , Consultez Interop Marshaling Avec Platform Invoke.

Cela signifie que vous devez vous assurer que votre délégué ne sera pas récupéré jusqu'à ce que le code géré soit appelé, en le gardant référencé dans votre code ou en l'épinglant.

5
shf301

Voir Marshal.GetFunctionPointerForDelegate , qui vous donnera un pointeur de fonction pour les appels gérés (code C #) à partir de code non géré.

1
Håvard S
0
t0mm13b