如何:使用 C++ Interop 封送處理回呼和委派

本主題示範使用 Visual C++ 在 Managed 和 Unmanaged 程式碼之間封送回呼和委派的封送處理(Managed 版本回呼)。

下列程式碼範例會使用 Managed、Unmanaged #pragma 指示詞,在相同的檔案中實作 Managed 和 Unmanaged 函式,但函式也可以在不同的檔案中定義。 僅包含 Unmanaged 函式的 檔案不需要使用 /clr (Common Language Runtime Compile) 進行編譯。

範例:設定 Unmanaged API 以觸發受控委派

下列範例示範如何設定 Unmanaged API 以觸發受控委派。 系統會建立 Managed 委派,並使用其中一個 Interop 方法 GetFunctionPointerForDelegate 來擷取委派的基礎進入點。 接著,此位址會傳遞至 Unmanaged 函式,其會呼叫該函式,但不知道實作為 Managed 函式的事實。

請注意,您可以使用 pin_ptr (C++/CLI) 釘選委派 ,以防止垃圾收集行程重新放置或處置委派。 需要保護過早垃圾收集,但釘選可提供比必要更多的保護,因為它可防止收集,但也防止重新配置。

如果委派由垃圾收集重新置放,它不會影響基礎 Managed 回呼,因此 Alloc 會用來新增委派的參考,以允許重新配置委派,但防止處置。 使用 GCHandle 而不是pin_ptr可減少受控堆積的分散潛力。

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

範例:Unmanaged API 所儲存的函式指標

下列範例與上一個範例類似,但在此情況下,提供的函式指標會由 Unmanaged API 儲存,因此可以隨時叫用它,要求在任意時間內隱藏垃圾收集。 因此,下列範例會使用 的全域實例 GCHandle ,以防止委派重新配置,與函式範圍無關。 如第一個範例中所討論,使用這些範例不需要pin_ptr,但在此情況下將無法運作,因為pin_ptr的範圍僅限於單一函式。

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

另請參閱

使用 C++ Interop (隱含 PInvoke)