How to: Marshal embedded pointers using P/Invoke

Funktionen, die in nicht verwalteten DLLs implementiert werden, können über verwalteten Code mithilfe von Plattform-Aufruffunktionen (P/Invoke) aufgerufen werden. Wenn der Quellcode für die DLL nicht verfügbar ist, ist P/Invoke die einzige Option für die Interoperabilität. Im Gegensatz zu anderen .NET-Sprachen bietet Visual C++ jedoch eine Alternative zu P/Invoke. Weitere Informationen finden Sie unter Using C++ Interop (Implicit P/Invoke) and How to: Marshal embedded pointers using C++ Interop.

Beispiel

Das Übergeben von Strukturen an systemeigenem Code erfordert, dass eine verwaltete Struktur, die hinsichtlich des Datenlayouts der systemeigenen Struktur entspricht, erstellt wird. Strukturen, die Zeiger enthalten, erfordern jedoch eine spezielle Handhabung. Für jeden eingebetteten Zeiger in der systemeigenen Struktur sollte die verwaltete Version der Struktur eine Instanz des IntPtr Typs enthalten. Außerdem müssen Speicher für diese Instanzen explizit zugewiesen, initialisiert und freigegeben werden, indem sie die AllocCoTaskMemMethoden , StructureToPtrund FreeCoTaskMem die Methoden verwenden.

Der folgende Code besteht aus einem nicht verwalteten und einem verwalteten Modul. Das nicht verwaltete Modul ist eine DLL, die eine Funktion definiert, die eine Struktur akzeptiert, die einen Zeiger ListString enthält, und eine Funktion, die aufgerufen wird 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]);
}

Das verwaltete Modul ist eine Befehlszeilenanwendung, die die Funktion importiert und eine Struktur MListStruct definiert, die TakesListStruct dem systemeigenen ListStruct entspricht, außer dass dies double* mit einer IntPtr Instanz dargestellt wird. Bevor die Funktion aufgerufen wird TakesListStruct, wird der main Speicher, auf den dieses Feld verweist, zugewiesen und initialisiert.

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

Für den verwalteten Code wird kein Teil der DLL mit der herkömmlichen #include Direktive verfügbar gemacht. Tatsächlich wird zur Laufzeit nur auf die DLL zugegriffen, sodass Probleme in funktionen, die mithilfe der Verwendung DllImportAttribute importiert werden, zur Kompilierungszeit nicht erkannt werden können.

Siehe auch

Verwenden expliziter P/Invoke in C++ (DllImport Attribut)