Udostępnij za pośrednictwem


Instrukcje: przeprowadzanie marshalingu wskaźników funkcji przy użyciu funkcji P/Invoke

Zarządzane delegaty mogą być używane zamiast wskaźników funkcji podczas współdziałania z funkcjami niezarządzanymi przy użyciu funkcji P/Invoke programu .NET Framework. Zachęcamy jednak do korzystania z funkcji międzyoperacyjności języka C++, jeśli jest to możliwe. Funkcja P/Invoke zapewnia niewielkie raportowanie błędów czasu kompilacji, nie jest bezpieczne dla typu i nie może być żmudne do zaimplementowania. Jeśli niezarządzany interfejs API jest spakowany jako biblioteka DLL, a kod źródłowy nie jest dostępny, P/Invoke jest jedyną opcją. W przeciwnym razie zobacz następujące artykuły:

Niezarządzane interfejsy API, które przyjmują wskaźniki funkcji jako argumenty, mogą być wywoływane z kodu zarządzanego przy użyciu zarządzanego delegata zamiast wskaźnika funkcji natywnej. Kompilator automatycznie marshaluje delegata do funkcji niezarządzanych jako wskaźnik funkcji. Wstawia niezbędny zarządzany/niezarządzany kod przejścia.

Przykład

Poniższy kod składa się z niezarządzanego i zarządzanego modułu. Moduł niezarządzany to biblioteka DLL, która definiuje funkcję o nazwie TakesCallback , która akceptuje wskaźnik funkcji. Ten adres służy do wykonywania funkcji.

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

Moduł zarządzany definiuje delegata, który jest marshaled do kodu natywnego jako wskaźnik funkcji. Używa atrybutu DllImportAttribute , aby uwidocznić funkcję natywną TakesCallback w kodzie zarządzanym. main W funkcji jest tworzone wystąpienie delegata i przekazywane do TakesCallback funkcji. Dane wyjściowe programu pokazują, że ta funkcja jest wykonywana przez funkcję natywną TakesCallback .

Funkcja zarządzana pomija odzyskiwanie pamięci dla zarządzanego delegata, aby zapobiec przeniesieniu delegata programu .NET Framework podczas wykonywania funkcji natywnej.

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

Żadna część biblioteki DLL nie jest uwidaczniona w kodzie zarządzanym przy użyciu tradycyjnej #include dyrektywy. W rzeczywistości biblioteka DLL jest dostępna tylko w czasie wykonywania, więc problemy z funkcjami importowanymi przy użyciu polecenia DllImportAttribute nie mogą być wykrywane w czasie kompilacji.

Zobacz też

Używanie jawnego P/Invoke w języku C++ (DllImport atrybut)