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

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

السلاسل المدارة و غير المدارة يتم تخطيطها بشكل مختلف في الذاكرة, لذا فإن تمرير سلاسل من دالات مُدارة إلى غير مدارة يتطلب السمة‬ MarshalAsAttribute لإرشاد برنامج التحويل البرمجي لإدراج آليات التحويل المطلوبة لـتنظيم و إرسال بيانات السلسلة بشكل صحيح وأمن.

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

مثال

التعليمات البرمجية التالية تتألف من وحدات نمطية غير مدارة و مدارة. الوحدة النمطية غير المدارة هي DLL تعرف دالة تُسمى TakesAString تقبل سلسلة ANSI نمط C في شكل الحرف* (*char). الوحدة النمطية المدارة هي تطبيق سطر أوامر يستورد الدالة TakesAString ، ولكن يعرّفها على أنها تأخذ System.String مدارة بدلاً من *char. السمة MarshalAsAttribute يمكن استخدامها لتوضيح كيف يجب أن تُنظم السلسلة المدارة عندما يتم استدعاء TakesAString.

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

// TraditionalDll2.cpp
// compile with: /LD /EHsc
#include <windows.h>
#include <stdio.h>
#include <iostream>

using namespace std;

#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif

extern "C" {
   TRADITIONALDLL_API void TakesAString(char*);
}

void TakesAString(char* p) {
   printf_s("[unmanaged] %s\n", p);
}

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

value struct TraditionalDLL
{
   [DllImport("TraditionalDLL2.dll")]
      static public void 
      TakesAString([MarshalAs(UnmanagedType::LPStr)]String^);
};

int main() {
   String^ s = gcnew String("sample string");
    Console::WriteLine("[managed] passing managed string to unmanaged function...");
   TraditionalDLL::TakesAString(s);
   Console::WriteLine("[managed] {0}", s);
}

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

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

راجع أيضًا:

موارد أخرى

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