Condividi tramite


Procedura: Effettuare il marshalling di matrici tramite P/Invoke

È possibile chiamare funzioni native che accettano stringhe di tipo C usando il tipo di String stringa CLR quando si usa il supporto P/Invoke (Platform Invoke) di .NET Framework. È consigliabile usare le funzionalità di interoperabilità C++ anziché P/Invoke, quando possibile. P/Invoke fornisce una segnalazione di errori in fase di compilazione, non è indipendente dai tipi e può essere noiosa da implementare. Se l'API non gestita viene inserita in un pacchetto come DLL e il codice sorgente non è disponibile, P/Invoke è l'unica opzione. In caso contrario, vedere Uso dell'interoperabilità C++ (P/Invoke implicito)).

Esempio

Poiché le matrici native e gestite sono disposte in modo diverso in memoria, passandole correttamente attraverso il limite gestito/non gestito richiede la conversione o il marshalling. Questo articolo illustra come passare una matrice di elementi semplici (blitable) alle funzioni native dal codice gestito.

Come è vero per il marshalling dei dati gestiti/non gestiti in generale, l'attributo DllImportAttribute viene usato per creare un punto di ingresso gestito per ogni funzione nativa usata. Nelle funzioni che accettano matrici come argomenti, l'attributo MarshalAsAttribute deve essere usato per specificare come effettuare il marshalling dei dati. Nell'esempio seguente l'enumerazione UnmanagedType viene usata per indicare che la matrice gestita viene sottoposto a marshalling come matrice in stile C.

Il codice seguente è costituito da un modulo non gestito e gestito. Il modulo non gestito è una DLL che definisce una funzione che accetta una matrice di numeri interi. Il secondo modulo è un'applicazione della riga di comando gestita che importa questa funzione, ma la definisce in termini di matrice gestita. Usa l'attributo MarshalAsAttribute per specificare che la matrice deve essere convertita in una matrice nativa quando viene chiamata.

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

Il modulo gestito viene compilato tramite /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]);
}

Nessuna parte della DLL viene esposta al codice gestito tramite la direttiva tradizionale #include . Infatti, poiché la DLL è accessibile solo in fase di esecuzione, i problemi nelle funzioni importate tramite DllImportAttribute non possono essere rilevati in fase di compilazione.

Vedi anche

Uso esplicito di P/Invoke in C++ (DllImport attributo)