Bagikan melalui


Cara: Penunjuk fungsi Marshal menggunakan P/Invoke

Delegasi terkelola dapat digunakan sebagai pengganti penunjuk fungsi saat beroperasi dengan fungsi yang tidak dikelola dengan menggunakan fitur .NET Framework P/Invoke. Namun, kami mendorong Anda untuk menggunakan fitur Interop C++ sebagai gantinya, jika memungkinkan. P/Invoke menyediakan sedikit pelaporan kesalahan waktu kompilasi, tidak aman untuk jenis, dan dapat melelahkan untuk diterapkan. Jika API yang tidak dikelola dikemas sebagai DLL dan kode sumber tidak tersedia, P/Invoke adalah satu-satunya opsi. Jika tidak, lihat artikel berikut:

API tidak terkelola yang mengambil penunjuk fungsi sebagai argumen dapat dipanggil dari kode terkelola dengan menggunakan delegasi terkelola sebagai pengganti penunjuk fungsi asli. Kompiler secara otomatis melakukan marsekal delegasi ke fungsi yang tidak dikelola sebagai penunjuk fungsi. Ini menyisipkan kode transisi terkelola/tidak terkelola yang diperlukan.

Contoh

Kode berikut terdiri dari modul yang tidak dikelola dan terkelola. Modul yang tidak dikelola adalah DLL yang menentukan fungsi yang disebut TakesCallback yang menerima penunjuk fungsi. Alamat ini digunakan untuk menjalankan fungsi.

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

Modul terkelola mendefinisikan delegasi yang di-marshal ke kode asli sebagai penunjuk fungsi. Ini menggunakan DllImportAttribute atribut untuk mengekspos fungsi asli TakesCallback ke kode terkelola. main Dalam fungsi , instans delegasi dibuat dan diteruskan ke TakesCallback fungsi . Output program menunjukkan bahwa fungsi ini dijalankan oleh fungsi asli TakesCallback .

Fungsi terkelola menekan pengumpulan sampah untuk delegasi terkelola untuk mencegah pengumpulan sampah .NET Framework memindahkan delegasi saat fungsi asli dijalankan.

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

Tidak ada bagian DLL yang diekspos ke kode terkelola menggunakan arahan tradisional #include . Bahkan, DLL diakses hanya pada runtime, sehingga masalah dengan fungsi yang diimpor dengan menggunakan DllImportAttribute tidak dapat dideteksi pada waktu kompilasi.

Lihat juga

Menggunakan P/Invoke eksplisit di C++ (DllImport atribut)