Sdílet prostřednictvím


Postupy: Zařazení funkce ukazatelů pomocí PInvoke

Toto téma vysvětluje, jak mohou být spravovaní delegáté použity v ukazatelích funkce, při spolupráci s nespravovanými funkcemi, použitím funkcí rozhraní .NET Framework P/Invoke. Nicméně, programátoři jazyka Visual C++ místo toho používají funkce Interop jazyka C++ (pokud je to možné), protože P/Invoke poskytuje malé vykazování chyb při rychlosti kompilace, což není typově bezpečné a může to být zdlouhavé pro implementaci. Pokud je nespravované rozhraní API zabaleno jako DLL knihovna a není k dispozici zdrojový kód, pak je P/Invoke jedinou možností. Jinak další informace naleznete v následujících tématech:

Nespravovaná rozhraní API, která přebírají ukazatele funkce jako argumenty, mohou být volány spravovaným kódem se spravovaným delegátem místo ukazatele nativní funkce. Kompilátor automaticky zařazuje delegáta do nespravované funkce jako ukazatel funkce a vloží nezbytný spravovaný/nespravovaný přechod kódu.

Příklad

Následující kód se skládá z nespravovaného a spravovaného modulu. Nespravovaný modul je knihovna DLL, která definuje funkce nazvané TakesCallback, přijímající ukazatel funkce. Tato adresa se používá ke spuštění funkce.

Spravovaný modul definuje delegáta, který je zařazen do nativního kódu jako ukazatel funkce a používá atribut DllImportAttribute k odhalení nativní funkce TakesCallback pro spravovaný kód. V hlavní funkci je instance delegáta vytvořena a předána do funkce TakesCallback. Výstup programu ukazuje, že tato funkce získá vykonané pomocí nativní funkce TakesCallback.

Spravovaná funkce potlačí uvolňování paměti pro spravovaného delegáta k zabránění uvolnění paměti rozhraní .NET Framework z přemístění delegáta, zatímco je vykonávána nativní funkce.

Spravovaný modul je kompilován se /clr, ale /clr:pure pracuje stejně dobře.

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

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

Všimněte si, že žádná část knihovny DLL není zpřístupněna spravovanému kódu použitím tradiční direktivy #include. Ve skutečnosti je knihovna DLL přístupná pouze za běhu, takže problémy s funkcemi, které jsou importovány s DllImportAttribute nejsou během kompilace odhaleny.

Viz také

Další zdroje

Použití explicitního PInvoke v jazyce C++ (atribut DllImport)