Bagikan melalui


Cara: Penunjuk yang disematkan Marshal menggunakan P/Invoke

Fungsi yang diimplementasikan dalam DLL yang tidak dikelola dapat dipanggil dari kode terkelola menggunakan fungsionalitas Platform Invoke (P/Invoke). Jika kode sumber untuk DLL tidak tersedia, P/Invoke adalah satu-satunya opsi untuk mengoperasikan. Namun, tidak seperti bahasa .NET lainnya, Visual C++ menyediakan alternatif untuk P/Invoke. Untuk informasi selengkapnya, lihat Menggunakan Interop C++ (Implisit P/Invoke) dan Cara: Pointer tersemat Marshal menggunakan Interop C++.

Contoh

Meneruskan struktur ke kode asli mengharuskan struktur terkelola yang setara dalam hal tata letak data ke struktur asli dibuat. Namun, struktur yang berisi pointer memerlukan penanganan khusus. Untuk setiap penunjuk yang disematkan dalam struktur asli, versi struktur terkelola harus berisi instans jenis IntPtr . Selain itu, memori untuk instans ini harus dialokasikan, diinisialisasi, dan dirilis secara eksplisit menggunakan AllocCoTaskMemmetode , StructureToPtr, dan FreeCoTaskMem .

Kode berikut terdiri dari modul yang tidak dikelola dan terkelola. Modul yang tidak dikelola adalah DLL yang mendefinisikan fungsi yang menerima struktur yang disebut ListString yang berisi penunjuk, dan fungsi yang disebut TakesListStruct.

// TraditionalDll6.cpp
// compile with: /EHsc /LD
#include <stdio.h>
#include <iostream>
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif

#pragma pack(push, 8)
struct ListStruct {
   int count;
   double* item;
};
#pragma pack(pop)

extern "C" {
   TRADITIONALDLL_API void TakesListStruct(ListStruct);
}

void TakesListStruct(ListStruct list) {
   printf_s("[unmanaged] count = %d\n", list.count);
   for (int i=0; i<list.count; i++)
      printf_s("array[%d] = %f\n", i, list.item[i]);
}

Modul terkelola adalah aplikasi baris perintah yang mengimpor TakesListStruct fungsi dan mendefinisikan struktur yang disebut MListStruct setara dengan aslinya ListStruct kecuali bahwa double* diwakili dengan IntPtr instans. Sebelum memanggil TakesListStruct, main fungsi mengalokasikan dan menginisialisasi memori yang dirujuk bidang ini.

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

[StructLayout(LayoutKind::Sequential, Pack=8)]
value struct MListStruct {
   int count;
   IntPtr item;
};

value struct TraditionalDLL {
    [DllImport("TraditionalDLL6.dll")]
   static public void TakesListStruct(MListStruct);
};

int main() {
   array<double>^ parray = gcnew array<double>(10);
   Console::WriteLine("[managed] count = {0}", parray->Length);

   Random^ r = gcnew Random();
   for (int i=0; i<parray->Length; i++) {
      parray[i] = r->NextDouble() * 100.0;
      Console::WriteLine("array[{0}] = {1}", i, parray[i]);
   }

   int size = Marshal::SizeOf(double::typeid);
   MListStruct list;
   list.count = parray->Length;
   list.item = Marshal::AllocCoTaskMem(size * parray->Length);

   for (int i=0; i<parray->Length; i++) {
      IntPtr t = IntPtr(list.item.ToInt32() + i * size);
      Marshal::StructureToPtr(parray[i], t, false);
   }

   TraditionalDLL::TakesListStruct( list );
   Marshal::FreeCoTaskMem(list.item);
}

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

Baca juga

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