Delen via


Hoe functieaanwijzers marshallen met behulp van P/Invoke

Beheerde gemachtigden kunnen worden gebruikt in plaats van functiepointers wanneer ze samenwerken met niet-beheerde functies met behulp van .NET Framework P/Invoke-functies. We raden u echter aan om in plaats daarvan de C++ Interop-functies te gebruiken, indien mogelijk. P/Invoke biedt weinig rapportage van compilatiefouten, is niet type-veilig en kan vervelend zijn om te implementeren. Als de niet-beheerde API is verpakt als een DLL en de broncode niet beschikbaar is, is P/Invoke de enige optie. Zie anders de volgende artikelen:

Niet-beheerde API's die functiepointers als argumenten gebruiken, kunnen worden aangeroepen vanuit beheerde code met behulp van een beheerde gemachtigde in plaats van de systeemeigen functiepointer. De compiler verwijst de gemachtigde automatisch naar onbeheerde functies als functiepointer. Hiermee wordt de benodigde beheerde/onbeheerde overgangscode ingevoegd.

Voorbeeld

De volgende code bestaat uit een niet-beheerde en een beheerde module. De niet-beheerde module is een DLL die een functie genaamd TakesCallback definieert die een functiepointer accepteert. Dit adres wordt gebruikt om de functie uit te voeren.

// TraditionalDll5.cpp
// compile with: /LD /EHsc
#include <iostream>
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif

extern "C" {
   /* Declare an unmanaged function type that takes two int arguments
      Note the use of __stdcall for compatibility with managed code */
   typedef int (__stdcall *CALLBACK)(int);
   TRADITIONALDLL_API int TakesCallback(CALLBACK fp, int);
}

int TakesCallback(CALLBACK fp, int n) {
   printf_s("[unmanaged] got callback address, calling it...\n");
   return fp(n);
}

De beheerde module definieert een delegate die naar de native code als een functiepointer wordt doorgestuurd. Het gebruikt het DllImportAttribute kenmerk om de natuurlijke TakesCallback functie voor de beheerde code beschikbaar te maken. In de main functie wordt een instantie van de delegeat gemaakt en doorgegeven aan de TakesCallback functie. De programma-uitvoer laat zien dat deze functie wordt uitgevoerd door de systeemeigen TakesCallback functie.

De beheerde functie onderdrukt garbagecollection voor de beheerde gemachtigde om te voorkomen dat .NET Framework garbagecollection de gedelegeerde verplaatst terwijl de systeemeigen functie wordt uitgevoerd.

// MarshalDelegate.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;

public delegate int GetTheAnswerDelegate(int);
public value struct TraditionalDLL {
   [DllImport("TraditionalDLL5.dll")]
   static public int TakesCallback(GetTheAnswerDelegate^ pfn, int n);
};

int GetNumber(int n) {
   Console::WriteLine("[managed] callback!");
   static int x = 0;
   ++x;
   return x + n;
}

int main() {
   GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
   pin_ptr<GetTheAnswerDelegate^> pp = &fp;
   Console::WriteLine("[managed] sending delegate as callback...");

   int answer = TraditionalDLL::TakesCallback(fp, 42);
}

Er wordt geen deel van het DLL-bestand blootgesteld aan de beheerde code met behulp van de traditionele #include instructie. In feite wordt het DLL-bestand alleen tijdens runtime geopend, dus problemen met functies die worden geïmporteerd met behulp DllImportAttribute van, kunnen niet worden gedetecteerd tijdens het compileren.

Zie ook

Expliciete P/Invoke gebruiken in C++ (DllImport kenmerk)