كيفية القيام بما يلي: تنظيم وإرسال صفائف باستخدام PInvoke

هذا الموضوع يشرح كيفية استدعاء الدالات الأصلية التى تقبل سلاسل نمط C باستخدام نوع سلسلة CLR String باستخدام دعم استدعاء للنظام الأساسي .NET Framework . نشجع مبرمجي ++Visual C على استخدام ميزات توافق C++ بدلاً من ذلك (عند الإمكان) لأن P/Invoke يوفر تقارير صغيرة لأخطاء وقت التحويل البرمجي , و هى غير آمنة النوع و شاقة التنفيذ. إذا تم حزم API الغير المدار كـ DLL، و مصدر التعليمات البرمجية غير متوفر فان P/Invoke هو الخيار الوحيد(و إلا ,راجعاستخدام PInvoke) C++ Interop الضمني )).

مثال

و لأن تخطيط الصفائف الأصلية و المدارة يكون بشكل مختلف في الذاكرة، فان تمريرها بنجاح عبر الحدود مُدارة/غير مُدارة يتطلب التحويل أو التنظيم و الإرسال. يوضح هذا الموضوع كيفية تمرير صفيف من عناصر بسيطة (blitable) إلى الدالات الأصلية من التعليمات البرمجية المدارة.

كما هو صحيح فى تنظيم وإرسال البيانات المُدارة/الغير المُدارة بشكل عام، يتم استخدام السمة DllImportAttribute لإنشاء نقطة إدخال مُدارة لكل دالة أصلية يتم استخدامها. في الحالة التي تأخذ فيها الدالات الصفائف كوسائط , يجب استخدام السمة MarshalAsAttribute أيضاً لتعين للمحول البرمجي كيفية تنظيم و إرسال البيانات. في المثال التالي , يتم استخدام التعداد LPArray للإشارة إلى أن الصفيف المدار سيتم تنظيمه و إرساله كصفيف نمط C.

التعليمات البرمجية التالية تتألف من وحدات نمطية مدارة و غير مدارة. الوحدة النمطية الغير المدارة هي DLL تعرّف الدالة التي تقبل صفيفاً من الأعداد الصحيحة. الوحدة النمطية الثانية هى تطبيق سطر الأوامر المدار الذى يقوم باستيراد هذه الدالة ولكن يعرّفها بدلالة صفيف مدار و يستخدم السمة MarshalAsAttribute لتحديد تحويل الصفيف إلى صفيف أصلي عند الاستدعاء.

يتم برمجيا ترجمة الوحدة النمطية المُدارة بواسطة /clr ولكن /clr:pure يصلح أيضاً .

// TraditionalDll4.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" {
   TRADITIONALDLL_API void TakesAnArray(int len, int[]);
}

void TakesAnArray(int len, int a[]) {
   printf_s("[unmanaged]\n");
   for (int i=0; i<len; i++)
      printf("%d = %d\n", i, a[i]);
}

// MarshalBlitArray.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;

value struct TraditionalDLL {
   [DllImport("TraditionalDLL4.dll")]
   static public void TakesAnArray(
   int len,[MarshalAs(UnmanagedType::LPArray)]array<int>^);
};

int main() {
   array<int>^ b = gcnew array<int>(3);
   b[0] = 11;
   b[1] = 33;
   b[2] = 55;
   TraditionalDLL::TakesAnArray(3, b);

   Console::WriteLine("[managed]");
   for (int i=0; i<3; i++)
      Console::WriteLine("{0} = {1}", i, b[i]);
}

لاحظ انه لا يوجد جزء من DLL يتعرض للتعليمات البرمجية المدارة باستخدام التوجيه التقليدى include#. في الحقيقة، لأنه يمكن الوصول إلى DLL في وقت تشغيل فقط ،فإن المشاكل مع الدالات المستوردة مع DllImportAttribute لن يتم الكشف عنها في وقت التحويل البرمجي.

راجع أيضًا:

موارد أخرى

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