Sdílet prostřednictvím


Postupy: Zařazení struktur pomocí služby PInvoke

Tento dokument vysvětluje, jak nativní funkce, které přijímají řetězce ve stylu C lze volat z spravované funkce, které poskytují instanci String pomocí P/Invoke.Přestože společnost Microsoft doporučuje použít funkce Interop jazyka C++ místo P/Invoke, protože P/Invoke poskytuje malé kompilace chybách, není typově bezpečné a může to být zdlouhavé pro implementaci, pokud je nespravované rozhraní API zabaleno jako DLL knihovna a není k dispozici zdrojový kód, je P/Invoke jedinou možností.V opačném případě naleznete v následujících dokumentech:

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

Tento dokument popisuje kroky potřebné k definování spravovaných ekvivalentů nativních struktur a jak lze předat výsledné struktury nespravované 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á, v Použití zprostředkovatele komunikace C++ (implicitní služba PInvoke).P/Invoke nelze mít non-blittable typy jako návratovou hodnotu.Blittable typy mají stejnou reprezentaci v spravovaného a nespravovaného kódu.Další informace naleznete v tématu Přenositelné a nepřenositelné typy.

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 za účelem deklarace vstupních bodů, které odpovídají jakékoli nespravované funkci, která přijímá strukturu, ale používá spravovanou verzi struktury v podpisech funkce, což 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 volání PInvoke v jazyce C++ (atribut DllImport)