Sdílet prostřednictvím


Jak: Marshal struktur pomocí PInvoke

Tento dokument vysvětluje, jak nativní funkce, které styl c řetězce lze volat z spravované funkcí, které poskytují instanci String pomocí P/Invoke.Přestože doporučujeme použít funkce spolupráce C++ namísto P/vyvolat, protože poskytuje P/Invoke hlášení není typ bezpečné a může být únavné implementovat, pokud je zabalen neřízené API jako knihovna DLL a zdrojový kód není k dispozici málo Chyba při kompilaci, P/Invoke je jediná možnost.Jinak viz následující dokumenty:

Ve výchozím nastavení jsou nativní a spravované struktury odlišně rozloženy v paměťi, tak úspěšné předání struktur přes spravované/nespravované hranice vyžaduje další kroky k zachování integrity dat.

Tento dokument popisuje kroky potřebné k definování spravované ekvivalenty nativní struktur a jak výsledné struktury mohou být předány do nespravovaného funkce.Tento dokument předpokládá, že jednoduché struktury – ty, které neobsahují řetězce nebo ukazatele – jsou používány.Informace o interoperability nepřenositelná, Použití interoperability C++ (implicitní PInvoke).Typy nepřenositelná nemůže být vrácená hodnota P/Invoke.Typy přímo přenositelná mají stejné zastoupení spravovaného a nespravovaného kódu.Další informace naleznete v tématu Přímo přenositelná a nepřenositelná typů.

Zařazování jednoduchých blittable struktur přes spravované/nespravované hranice nejprve vyžaduje, že budou definovány spravované verze každé nativní struktury.Tyto struktury mohou mít libovolný platný název; neexistuje vztah mezi nativní a spravovanou verzí dvou struktur jiných než jejich rozvržení dat.Proto je důležité, aby spravované verze obsahovaly pole, která mají stejné rozměry a jsou ve stejném pořadí jako nativní verze.(Není zde žádný mechanismus zajišťující, že spravované a nativní verze struktur jsou ekvivalentní, takže nekompatibilita není zřejmá až do spuštění.Je na odpovědnosti programátora, aby zajistil, že dvě struktury mají stejné rozvržení dat.)

Protože jsou členové spravovaných struktur někdy přeskupení z důvodů výkonu, je nutné použít atribut StructLayoutAttribute, abyste označili, že struktura je rozložena sekvenčně.Je také vhodné explicitně nastavit nastavení obalování struktury, aby bylo stejné jako to, které používá nativní struktura.(Ve výchozím nastavení používá Visual C++ 8bajtové obalování struktury pro spravovaný kód.)

  1. Poté použijte DllImportAttribute, abyste deklarovali vstupní body, které odpovídají jakékoli nespravované funkci, která přijmá strukturu, ale používá spravovanou verzi struktury v podpisech funkce, která je bod moot, pokud použijete stejný název pro obě verze struktury.

  2. Nyní může spravovaný kód předat spravovanou verzi struktury nespravované funkci, jakoby byly skutečně spravované funkce.Tyto struktury mohou být předány hodnotou nebo odkazem, jak je ukázáno v následujícím příkladu.

Příklad

Následující kód se skládá z nespravovaného a spravovaného modulu.Nespravovaný modul je knihovna DLL, která definuje strukturu s názvem Location a funkci s názvem GetDistance, která přijímá dvě instance struktury Location.Druhý modul je spravovaná aplikace příkazového řádku, která importuje funkci GetDistance, ale definuje ji z hlediska spravovaného ekvivalentu struktury Location, MLocation.V praxi by byl stejný název pravděpodobně použit pro obě verze struktury; jiný název se zde však používá, aby se ukázalo, že prototyp DllImport je definován z hlediska spravované verze.

Spravovaný modul je kompilován se /clr, ale /clr:pure pracuje stejně dobře.

Všimněte si, že žádná část knihovny DLL není zpřístupněna spravovanému kódu použitím tradiční direktivy #include.Ve skutečnosti je knihovna DLL přístupná pouze za běhu, takže problémy s funkcemi, které jsou importovány s DllImport nejsou během kompilace odhaleny.

// TraditionalDll3.cpp
// compile with: /LD /EHsc
#include <iostream>
#include <stdio.h>
#include <math.h>

#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
   #define TRADITIONALDLL_API __declspec(dllexport)
#else
   #define TRADITIONALDLL_API __declspec(dllimport)
#endif

#pragma pack(push, 8)
struct Location {
   int x;
   int y;
};
#pragma pack(pop)

extern "C" {
   TRADITIONALDLL_API double GetDistance(Location, Location);
   TRADITIONALDLL_API void InitLocation(Location*);
}

double GetDistance(Location loc1, Location loc2) {
   printf_s("[unmanaged] loc1(%d,%d)", loc1.x, loc1.y);
   printf_s(" loc2(%d,%d)\n", loc2.x, loc2.y);

   double h = loc1.x - loc2.x;
   double v = loc1.y = loc2.y;
   double dist = sqrt( pow(h,2) + pow(v,2) );

   return dist;
}

void InitLocation(Location* lp) {
   printf_s("[unmanaged] Initializing location...\n");
   lp->x = 50;
   lp->y = 50;
}

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

[StructLayout(LayoutKind::Sequential, Pack=8)]
value struct MLocation {
   int x;
   int y;
};

value struct TraditionalDLL {
   [DllImport("TraditionalDLL3.dll")]
   static public double GetDistance(MLocation, MLocation);
   [DllImport("TraditionalDLL3.dll")]
   static public double InitLocation(MLocation*);
};

int main() {
   MLocation loc1;
   loc1.x = 0;
   loc1.y = 0;

   MLocation loc2;
   loc2.x = 100;
   loc2.y = 100;

   double dist = TraditionalDLL::GetDistance(loc1, loc2);
   Console::WriteLine("[managed] distance = {0}", dist);

   MLocation loc3;
   TraditionalDLL::InitLocation(&loc3);
   Console::WriteLine("[managed] x={0} y={1}", loc3.x, loc3.y);
}
  
  

Viz také

Další zdroje

Použití explicitního PInvoke v jazyce C++ (atribut DllImport)