كيفية القيام بما يلي: تنظيم وإرسال مؤشرات دالة باستخدام 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 لن يتم الكشف عنها في وقت التحويل البرمجي.

راجع أيضًا:

موارد أخرى

استخدام PInvoke Explicit في ++C (سمة DllImport)