Bagikan melalui


Cara: Panggilan Balik dan Delegasi Marshal Dengan Menggunakan Interop C++

Topik ini menunjukkan marsekal panggilan balik dan delegasi (versi terkelola panggilan balik) antara kode terkelola dan tidak terkelola menggunakan Visual C++.

Contoh kode berikut menggunakan arahan #pragma terkelola dan tidak dikelola untuk mengimplementasikan fungsi terkelola dan tidak terkelola dalam file yang sama, tetapi fungsi juga dapat didefinisikan dalam file terpisah. File yang hanya berisi fungsi yang tidak dikelola tidak perlu dikompilasi dengan /clr (Kompilasi Runtime Bahasa Umum).

Contoh: Mengonfigurasi API yang tidak dikelola untuk memicu delegasi terkelola

Contoh berikut menunjukkan cara mengonfigurasi API yang tidak dikelola untuk memicu delegasi terkelola. Delegasi terkelola dibuat dan salah satu metode interop, GetFunctionPointerForDelegate, digunakan untuk mengambil titik masuk yang mendasar untuk delegasi. Alamat ini kemudian diteruskan ke fungsi yang tidak dikelola, yang menyebutnya tanpa pengetahuan tentang fakta bahwa itu diimplementasikan sebagai fungsi terkelola.

Perhatikan bahwa apakah mungkin, tetapi tidak perlu, untuk menyematkan delegasi menggunakan pin_ptr (C++/CLI) untuk mencegahnya ditemukan kembali atau dibuang oleh pengumpul sampah. Perlindungan dari pengumpulan sampah dini diperlukan, tetapi penyematan memberikan perlindungan lebih dari yang diperlukan, karena mencegah pengumpulan tetapi juga mencegah relokasi.

Jika delegasi ditemukan kembali oleh pengumpulan sampah, itu tidak akan memengaruhi panggilan balik terkelola yang mendasar, jadi Alloc digunakan untuk menambahkan referensi ke delegasi, memungkinkan relokasi delegasi, tetapi mencegah pembuangan. Menggunakan GCHandle alih-alih pin_ptr mengurangi potensi fragmentasi dari timbunan terkelola.

// MarshalDelegate1.cpp
// compile with: /clr
#include <iostream>

using namespace System;
using namespace System::Runtime::InteropServices;

#pragma unmanaged

// Declare an unmanaged function type that takes two int arguments
// Note the use of __stdcall for compatibility with managed code
typedef int (__stdcall *ANSWERCB)(int, int);

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

#pragma managed

public delegate int GetTheAnswerDelegate(int, int);

int GetNumber(int n, int m) {
   Console::WriteLine("[managed] callback!");
   return n + m;
}

int main() {
   GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
   GCHandle gch = GCHandle::Alloc(fp);
   IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
   ANSWERCB cb = static_cast<ANSWERCB>(ip.ToPointer());
   Console::WriteLine("[managed] sending delegate as callback...");

// force garbage collection cycle to prove
// that the delegate doesn't get disposed
   GC::Collect();

   int answer = TakesCallback(cb, 243, 257);

// release reference to delegate
   gch.Free();
}

Contoh: Penunjuk fungsi yang disimpan oleh API yang tidak dikelola

Contoh berikut mirip dengan contoh sebelumnya, tetapi dalam hal ini penunjuk fungsi yang disediakan disimpan oleh API yang tidak dikelola, sehingga dapat dipanggil kapan saja, mengharuskan pengumpulan sampah ditekan untuk waktu yang lama. Akibatnya, contoh berikut menggunakan instans GCHandle global untuk mencegah delegasi direlokasi, independen dari cakupan fungsi. Seperti yang dibahas dalam contoh pertama, menggunakan pin_ptr tidak perlu untuk contoh-contoh ini, tetapi dalam hal ini tidak akan berfungsi, karena cakupan pin_ptr terbatas pada satu fungsi.

// MarshalDelegate2.cpp
// compile with: /clr
#include <iostream>

using namespace System;
using namespace System::Runtime::InteropServices;

#pragma unmanaged

// Declare an unmanaged function type that takes two int arguments
// Note the use of __stdcall for compatibility with managed code
typedef int (__stdcall *ANSWERCB)(int, int);
static ANSWERCB cb;

int TakesCallback(ANSWERCB fp, int n, int m) {
   cb = fp;
   if (cb) {
      printf_s("[unmanaged] got callback address (%d), calling it...\n", cb);
      return cb(n, m);
   }
   printf_s("[unmanaged] unregistering callback");
   return 0;
}

#pragma managed

public delegate int GetTheAnswerDelegate(int, int);

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

   return n + m + x;
}

static GCHandle gch;

int main() {
   GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);

   gch = GCHandle::Alloc(fp);

   IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
   ANSWERCB cb = static_cast<ANSWERCB>(ip.ToPointer());
   Console::WriteLine("[managed] sending delegate as callback...");

   int answer = TakesCallback(cb, 243, 257);

   // possibly much later (in another function)...

   Console::WriteLine("[managed] releasing callback mechanisms...");
   TakesCallback(0, 243, 257);
   gch.Free();
}

Baca juga

Menggunakan interop C++ (PInvoke implisit)