Bagikan melalui


Cara: String Marshal menggunakan P/Invoke

Fungsi asli yang menerima string gaya C dapat dipanggil menggunakan jenis System::String string CLR dengan menggunakan dukungan .NET Framework Platform Invoke (P/Invoke). Kami mendorong Anda untuk menggunakan fitur Interop C++ alih-alih P/Invoke jika memungkinkan. karena P/Invoke menyediakan sedikit pelaporan kesalahan waktu kompilasi, tidak aman untuk jenis, dan dapat melelahkan untuk diterapkan. Jika API yang tidak dikelola dikemas sebagai DLL, dan kode sumber tidak tersedia, maka P/Invoke adalah satu-satunya opsi. Jika tidak, lihat Menggunakan Interop C++ (Implisit P/Invoke).

String terkelola dan tidak terkelola ditata secara berbeda dalam memori, sehingga meneruskan string dari fungsi yang dikelola ke tidak terkelola memerlukan atribut untuk menginstruksikan MarshalAsAttribute pengkompilasi untuk menyisipkan mekanisme konversi yang diperlukan untuk marshaling data string dengan benar dan aman.

Seperti halnya fungsi yang hanya menggunakan jenis data intrinsik, DllImportAttribute digunakan untuk mendeklarasikan titik masuk terkelola ke dalam fungsi asli. Fungsi yang meneruskan string dapat menggunakan handel ke String jenis alih-alih menentukan titik entri ini sebagai mengambil string gaya C. Menggunakan jenis ini meminta pengkompilasi untuk menyisipkan kode yang melakukan konversi yang diperlukan. Untuk setiap argumen fungsi dalam fungsi tidak terkelola yang mengambil string, gunakan MarshalAsAttribute atribut untuk menunjukkan bahwa String objek harus dirusak ke fungsi asli sebagai string gaya C.

Marshaler membungkus panggilan ke fungsi yang tidak dikelola dalam rutinitas pembungkus tersembunyi. Pembungkus rutin menyematkan dan menyalin string terkelola ke dalam string yang dialokasikan secara lokal dalam konteks yang tidak dikelola. Salinan lokal kemudian diteruskan ke fungsi yang tidak dikelola. Ketika fungsi yang tidak dikelola kembali, pembungkus menghapus sumber daya. Atau, jika berada di tumpukan, itu diklaim kembali ketika pembungkus keluar dari cakupan. Fungsi yang tidak dikelola tidak bertanggung jawab atas memori ini. Kode yang tidak dikelola hanya membuat dan menghapus memori dalam tumpukan yang disiapkan oleh CRT-nya sendiri, sehingga tidak pernah ada masalah dengan marshaller menggunakan versi CRT yang berbeda.

Jika fungsi Anda yang tidak dikelola mengembalikan string, baik sebagai nilai pengembalian atau parameter keluar, marshaler menyalinnya ke dalam string terkelola baru, lalu melepaskan memori. Untuk informasi selengkapnya, lihat Perilaku Marshaling Default dan Marshaling Data dengan Platform Invoke.

Contoh

Kode berikut terdiri dari modul yang tidak dikelola dan modul terkelola. Modul yang tidak dikelola adalah DLL yang menentukan fungsi yang disebut TakesAString. TakesAString menerima string sempit gaya C dalam bentuk char*.

// 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);
}

Modul terkelola adalah aplikasi baris perintah yang mengimpor TakesAString fungsi, tetapi mendefinisikannya sebagai mengambil yang dikelola System.String alih-alih char*. Atribut MarshalAsAttribute digunakan untuk menunjukkan bagaimana string terkelola harus di-marshal ketika TakesAString dipanggil.

// 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);
}

Teknik ini membuat salinan string pada tumpukan yang tidak dikelola, sehingga perubahan yang dilakukan pada string oleh fungsi asli tidak akan tercermin dalam salinan terkelola string.

Tidak ada bagian DLL yang diekspos ke kode terkelola oleh arahan tradisional #include . Bahkan, DLL diakses pada runtime saja, sehingga masalah dalam fungsi yang diimpor dengan menggunakan DllImport tidak terdeteksi pada waktu kompilasi.

Lihat juga

Menggunakan P/Invoke eksplisit di C++ (DllImport atribut)