Condividi tramite


Procedura: Effettuare il marshalling dei puntatori incorporati tramite P/Invoke

Le funzioni implementate nelle DLL non gestite possono essere chiamate dal codice gestito usando la funzionalità Platform Invoke (P/Invoke). Se il codice sorgente per la DLL non è disponibile, P/Invoke è l'unica opzione per l'interoperabilità. Tuttavia, a differenza di altri linguaggi .NET, Visual C++ offre un'alternativa a P/Invoke. Per altre informazioni, vedere Uso dell'interoperabilità C++ (P/Invoke implicito) e Procedura: Effettuare il marshalling dei puntatori incorporati usando l'interoperabilità C++.

Esempio

Il passaggio di strutture al codice nativo richiede la creazione di una struttura gestita equivalente in termini di layout di dati alla struttura nativa. Tuttavia, le strutture che contengono puntatori richiedono una gestione speciale. Per ogni puntatore incorporato nella struttura nativa, la versione gestita della struttura deve contenere un'istanza del IntPtr tipo. Inoltre, la memoria per queste istanze deve essere allocata, inizializzata e rilasciata in modo esplicito usando i AllocCoTaskMemmetodi , StructureToPtre FreeCoTaskMem .

Il codice seguente è costituito da un modulo non gestito e gestito. Il modulo non gestito è una DLL che definisce una funzione che accetta una struttura denominata che contiene un puntatore e una funzione denominata ListStringTakesListStruct.

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

Il modulo gestito è un'applicazione della riga di comando che importa la TakesListStruct funzione e definisce una struttura denominata MListStruct equivalente a quella nativa ListStruct , ad eccezione del fatto che è rappresentata con un'istanza double*IntPtr di . Prima di chiamare TakesListStruct, la main funzione alloca e inizializza la memoria a cui fa riferimento questo campo.

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

Nessuna parte della DLL viene esposta al codice gestito usando la direttiva tradizionale #include . Infatti, la DLL è accessibile solo in fase di esecuzione, quindi 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)