C# en C++: DLL’s met callback functionaliteit

In C# is het prima mogelijk om (unmanaged) C++ DLL’s te gebruiken.

Zolang er basistypen (bool, int, double) gebruikt worden in functieparameters en returntypes is er niet veel aan de hand.

Lastiger wordt het wanneer er complexere typen heen en weer moeten tussen managed (C#) code en unmanaged (C++) code. In dit artikeltje wordt bekeken op welke manier functie-pointers kunnen worden uitgewisseld.

Het scenario
Stel we hebben een DLL die een berekening uitvoert. Voor die berekening is het nodig om waardes op te zoeken in een omvangrijke database. Op voorhand is niet bekend om welke waardes het gaat.

Nu zijn er een aantal manieren denkbaar waarop de DLL aan zijn gegevens zou kunnen komen.

  • De DLL spreekt zelf de database aan;
  • De DLL krijgt de relevante tabellen door van de aanroepende partij;
  • De DLL laat het opzoeken over aan de aanroepende partij en gebruikt hiervoor een callback functie.

De laatste optie is handig als de aanroepende partij toch al gebruik maakt van de database.

Bovendien zorgt de keuze van een callback ook voor een betere ‘separation of concerns’. De DLL kan zich puur bezig houden met de berekening en hoeft niets te weten van een specifieke database. Als een DLL op deze manier gevrijwaard kan blijven van ‘oneigenlijke’ functionaliteit, is deze generieker toe te passen en neemt de herbruikbaarheid toe.

De implementatie
Uitganspunt is een C++ DLL met de volgende interface:

typedef double (*getHeight_t) (double lon, double lat);

extern “C”
{
_declspec(dllexport) double shortestGroundDistance(
         double lat1, double lon1,
         double lat2, double lon2,
         getHeight_t getHeight);
}

Deze DLL exporteert de functie shortestGroundDistance. Deze functie accepteert een functiepointer met type getHeight_t. getGeight_t is pointer naar een functie die doubles accepteert (lat en lon) en een double retourneert.
De implementatie van shortestGroundDistance zal de meegegeven getHeight functie aanroepen om daarmee de kortste afstand over de grond tussen twee punten mee te berekenen.

De functie shortestGroundDistance verwacht dus een functiepointer als argument. Een functiepointer in C++ is gelijkwaardig aan een delegate in C#. Een gewone (managed) delegate kan echter niet worden doorgegeven aan een DLL. We moeten gebruik maken van de attribuut ‘UnmanagedFunctionPointer’ om een delegate te krijgen die gelijk is aan een C++ function pointer.

Met deze kennis kan het C# gedeelte geimplementeerd worden:

class Program
{
   [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
   delegate double GetHeightFunction(double lat, double lon);
 
   [DllImport(“CalcDistance.dll”, 
    CallingConvention= CallingConvention.Cdecl)]
   static extern double shortestGroundDistance(
          double lat1, double lon1, double lat2, double lon2,
          GetHeightFunction getHeight);
 
   static double GetHeight(double lat, double lon)
   {
      // .. retrieve height at location lat, lon from a database
      // .. code omitted ..
      return height;
   }
 
   static void Main(string[] args)
   {
      // Testing the DLL. Note that the GetHeight
      // delegate is passed to the DLL.
      double dist = shortestGroundDistance(
          52.0, 5.0, 52.0, 6.0, GetHeight);
 
      Console.WriteLine(string.Format(“Distance = {0}”, dist);
   }
}

Let op de keuze van de juiste ‘calling convention’ bij zowel de delegate als de DLL functie.

Advertenties
Dit bericht werd geplaatst in C#, C++ en getagged met . Maak dit favoriet permalink.

Geef een reactie

Vul je gegevens in of klik op een icoon om in te loggen.

WordPress.com logo

Je reageert onder je WordPress.com account. Log uit / Bijwerken )

Twitter-afbeelding

Je reageert onder je Twitter account. Log uit / Bijwerken )

Facebook foto

Je reageert onder je Facebook account. Log uit / Bijwerken )

Google+ photo

Je reageert onder je Google+ account. Log uit / Bijwerken )

Verbinden met %s