كيفية القيام بما يلي: تنظيم وإرسال مؤشرات دالة باستخدام PInvoke
يشرح هذا المقال كيف يمكن استخدام المفوضين المدارين بدلاً من مؤشرات الدالة عند التشغيل التفاعلي مع دالات غير مُدارة باستخدام ميزات .NET Framework P/Invoke . و على كلٍ، يتم تشجيع مبرمجي ++Visual C لاستخدام ميزات توافق C++ (C++ Interop) بدلاً من ذلك (عند الإمكان) لأن P/Invoke يسبب بعض التقارير الخاطئة البسيطة وقت التحويل البرمجي , و هو غير آمن من جهة النوع و شاق لتنفيذ. إذا تم حزم API الغير المدار كـ DLL ، و مصدر التعليمات البرمجية غير متوفر، فإن P/Invoke هو الخيار الوحيد. وإلا, فراجع المواضيع التالية:
API الغير المدار الذى يأخذ مؤشرات دالات كوسائط، يمكن استدعاؤه من التعليمات البرمجية المُدارة بواسطة مفوض مدار بدلاً من مؤشر الدالة الأصلي. يقوم المحول البرمجي بتنظيم و إرسال المفوض أوتوماتيكياً إلى الدالات الغير المدارة كمؤشر دالة و يدرج التعليمات البرمجية الضرورية الخاصة بالانتقال المُدار/الغير المُدار.
مثال
التعليمات البرمجية التالية تتألف من وحدات نمطية مدارة و غير مدارة. الوحدة النمطية الغير المدارة هي DLL تعرِّف دالة تسمى TakesCallback تقبل مؤشر دالة. يتم استخدام هذا العنوان لتنفيذ الدالة.
الوحدة النمطية المدارة تُعرف مفوض يتم تنظيمه و إرساله إلى تعليمة برمجية أصلية كمؤشر دالة، و يستخدم السمة DllImportAttribute لعرض دالة TakesCallback الأصلية للتعليمات البرمجية المدارة. في الدالة الرئيسية (main)، يتم إنشاء مثيل للمفوض و تمريره إلى دالة TakesCallback . يوضح إخراج البرنامج أن هذه الدالة يتم تنفيذها بواسطة دالة TakesCallback الأصلية .
تمنع الدالة المدارة جمع المهملات للمفوض المدار لمنع جامع المهملات .NET Framework من تغيير موقع المفوض فى وقت تنفيذ الدالة الأصلية.
يتم برمجيا ترجمة الوحدة النمطية المُدارة بواسطة /clr ولكن /clr:pure يصلح أيضاً .
// 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);
}
// 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);
}
لاحظ انه لا يوجد جزء من DLL يتعرض للتعليمات البرمجية المدارة باستخدام توجيه # التقليدي. في الحقيقة، الوصول إلى DLL يتم في وقت التشغيل فقط ،لذلك المشاكل مع الدالات المستوردة معDllImportAttribute لن يتم الكشف عنها في وقت التحويل البرمجي.