Postupy: Zařazování ukazatelů na funkce pomocí volání nespravovaného kódu
Spravované delegáty je možné použít místo ukazatelů funkcí při spolupráci s nespravovanými funkcemi pomocí funkcí P/Invoke rozhraní .NET Framework. Pokud je to ale možné, doporučujeme místo toho používat funkce interoperability C++. Volání nespravovaného kódu poskytuje zasílání zpráv o chybách v době kompilace, není typově bezpečné a nelze je implementovat. Pokud je nespravované rozhraní API zabalené jako knihovna DLL a zdrojový kód není k dispozici, je jediným parametrem volání nespravovaného kódu. V opačném případě si přečtěte tyto články:
Použití zprostředkovatele komunikace C++ (implicitní volání nespravovaného kódu)
Postupy: Zařazování zpětných volání a delegátů pomocí zprostředkovatele komunikace C++
Nespravovaná rozhraní API, která přebírají ukazatele na funkce jako argumenty, je možné volat ze spravovaného kódu pomocí spravovaného delegáta místo ukazatele nativní funkce. Kompilátor automaticky zařadí delegáta na nespravované funkce jako ukazatel funkce. Vloží potřebný spravovaný nebo nespravovaný přechodový kód.
Příklad
Následující kód se skládá z nespravovaného a spravovaného modulu. Nespravovaný modul je knihovna DLL, která definuje funkci s názvem TakesCallback
, která přijímá ukazatel funkce. Tato adresa se používá ke spuštění funkce.
// 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);
}
Spravovaný modul definuje delegáta, který je zařazený do nativního kódu jako ukazatel funkce. Pomocí atributu DllImportAttribute zveřejňuje nativní TakesCallback
funkci spravovanému kódu. main
Ve funkci se vytvoří instance delegáta a předá funkciTakesCallback
. Výstup programu ukazuje, že tato funkce se spustí nativní TakesCallback
funkcí.
Spravovaná funkce potlačí uvolňování paměti spravovaného delegáta, aby zabránila uvolnění paměti rozhraní .NET Framework v přemístění delegáta při spuštění nativní funkce.
// 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);
}
Žádná část knihovny DLL není vystavena spravovanému kódu pomocí tradiční #include
direktivy. Knihovna DLL je ve skutečnosti přístupná pouze za běhu, takže problémy s funkcemi importovanými pomocí DllImportAttribute nelze zjistit v době kompilace.
Viz také
Použití explicitního volání nespravovaného kódu v jazyce C++ (DllImport
atribut)