كيفية القيام بما يلي: تنظيم وإرسال عمليات الاسترجاع و المفوضين بـاستخدام C++ Interop

هذا الموضوع يوضح تنظيم عمليات الاسترجاع(رد الاتصال) والمفوضات (الإصدار المدار من رد اتصال) بين التعليمات البرمجية المدارة و الغير مدارة باستخدام Visual C++‎.

أمثلة التعليمات البرمجية التالية تستخدم توجيهات #pragmaـ managed, unmanaged لتنفذ الدالات المدارة و غير المدارة في نفس الملف , ولكن يمكن أيضاً تعريفها في ملفات منفصلة. الملفات التي تحتوي على دالات غير مدارة فقط لا تحتاج إلى أن يتم تحويلها برمجياً باستخدام /clr (التجميع وقت تشغيل اللغة العامة).

مثال

يوضح المثال التالي كيفية تكوين API غير مدار لتشغيل مفوض مدار. يتم إنشاء مفوض مدار و أحد أساليب التوافق، GetFunctionPointerForDelegate يتم إستخدامه لاسترداد نقطة الإدخال الأساسية للمفوض. ثم يتم تمرير هذا العنوان للدالة الغير مُدارة التي تستدعيها بدون معرفة حقيقة أنه تم تطبيقها كدالة مدارة.

لاحظ أنه من الممكن،و لكن ليس من الضرورى، أن تثبت المفوض باستخدام pin_ptr لمنعه من تغيير موضعه أو التخلص منه من قبل مجمع البيانات المهملة. الحماية من تجميع البيانات المهملة قبل الأوان مطلوب و لكن التثبيت يوفر حماية أكثر من الضرورية لأنه يمنع التجميع وتغيير الموضع أيضاً.

إذا تم تغيير مكان مفوض بواسطة تجميع البيانات المهملة ، فإنه لا يؤثر على رد الاتصال المدار ، وبالتالي يتم إستخدام 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();
}

يشبه المثال التالي المثال السابق ولكن في هذه الحالة مؤشر الدالة الذي تم توفيره مخزن بواسطة 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();
}

راجع أيضًا:

المرجع

استخدام PInvoke) C++ Interop الضمني )