Udostępnij za pośrednictwem


Instrukcje: przeprowadzanie marshalingu tablic przy użyciu funkcji P/Invoke

Funkcje natywne, które akceptują ciągi w stylu C, można wywoływać przy użyciu typu String ciągu CLR podczas korzystania z obsługi wywołania platformy .NET Framework (P/Invoke). Zachęcamy do korzystania z funkcji międzyoperacyjności języka C++ zamiast funkcji P/Invoke, jeśli jest to możliwe. Funkcja P/Invoke zapewnia niewielkie raportowanie błędów czasu kompilacji, nie jest bezpieczne dla typu i nie może być żmudne do zaimplementowania. Jeśli niezarządzany interfejs API jest spakowany jako biblioteka DLL, a kod źródłowy nie jest dostępny, P/Invoke jest jedyną opcją. W przeciwnym razie zobacz Using C++ Interop (Implicit P/Invoke)).

Przykład

Ponieważ macierze natywne i zarządzane są rozmieszczone inaczej w pamięci, przekazywanie ich pomyślnie przez granicę zarządzaną/niezarządzaną wymaga konwersji lub marshalingu. W tym artykule pokazano, jak można przekazać tablicę prostych (blitable) elementów do funkcji natywnych z kodu zarządzanego.

Podobnie jak w przypadku zarządzanego/niezarządzanego marshalingu danych, DllImportAttribute atrybut jest używany do tworzenia zarządzanego punktu wejścia dla każdej używanej funkcji natywnej. W funkcjach, które przyjmują tablice jako argumenty, MarshalAsAttribute atrybut musi być używany do określania sposobu marshalingu danych. W poniższym przykładzie wyliczenie służy do wskazywania, UnmanagedType że tablica zarządzana jest marshalowana jako tablica w stylu C.

Poniższy kod składa się z niezarządzanego i zarządzanego modułu. Moduł niezarządzany to biblioteka DLL, która definiuje funkcję, która akceptuje tablicę liczb całkowitych. Drugi moduł to zarządzana aplikacja wiersza polecenia, która importuje tę funkcję, ale definiuje ją pod względem tablicy zarządzanej. Używa atrybutu MarshalAsAttribute , aby określić, że tablica powinna zostać przekonwertowana na macierz natywną po wywołaniu.

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

Zarządzany moduł jest kompilowany przy użyciu polecenia /clr.

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

Żadna część biblioteki DLL nie jest widoczna dla kodu zarządzanego za pomocą tradycyjnej #include dyrektywy. W rzeczywistości, ponieważ biblioteka DLL jest dostępna tylko w czasie wykonywania, problemy z funkcjami importowanymi przy użyciu DllImportAttribute nie można wykryć w czasie kompilacji.

Zobacz też

Używanie jawnego P/Invoke w języku C++ (DllImport atrybut)