Porady: kierowanie struktur za pomocą funkcji PInvoke
W tym dokumencie omówiono sposób macierzysty funkcji, które akceptują ciągi stylu C może zostać wywołana z zarządzanej funkcji, które zapewniają wystąpienie String przy użyciu P i Invoke.Chociaż zaleca się używanie funkcji C ++ Interop zamiast P i Invoke, ponieważ P i Invoke zawiera niewiele kompilacji raportowanie błędów, nie jest typu palety i może być uciążliwe do wykonania, jeśli API niezarządzanego, jest dostarczana w bibliotece DLL i kod źródłowy nie jest dostępny, P/Invoke jest jedyną opcją.W przeciwnym razie zobacz następujące dokumenty:
Domyślnie struktur macierzystych i zarządzane są określone inaczej w pamięci, więc pomyślnie przechodzi struktur na granicy zarządzanych/niezarządzanych wymaga dodatkowe kroki w celu zachowania integralności danych.
Ten dokument wyjaśnia kroki wymagane do definiowania zarządzanych odpowiedniki macierzystego struktur i jak struktur wynikowe mogą być przekazywane do funkcji niezarządzanego.Dokument ten zakłada, że prostych struktur — tych, które nie zawierają ciągi lub wskaźniki — są używane.Informacje dla interoperacyjności możliwość kopiowania, zobacz Korzystanie z międzyoperacyjności języka C++ (niejawna funkcja PInvoke).P/Invoke nie mogą mieć typów nie istnieć jako wartość zwracana.Typów istnieć mają taką samą reprezentację w kod zarządzany i niezarządzany.Aby uzyskać dodatkowe informacje, zobacz Typy kopiowalne i niekopiowalne.
Kierowanie proste, struktur istnieć na granicy zarządzanych/niezarządzanych najpierw wymaga zdefiniowania zarządzanych wersje każdej budowli macierzystego.Te struktury może mieć dowolną nazwę prawnych; nie ma żadnego związku między wersją macierzystego i zarządzanego struktur dwóch innych niż ich układ danych.Dlatego jest istotne, że zarządzanych wersji zawiera pola, które są taki sam rozmiar i w takiej samej kolejności jak macierzystą wersję. (Nie istnieje mechanizm dla zapewnienia, że wersje zarządzanego i macierzystym struktury są równoważne, więc niezgodności będą widoczne do czasu wykonywania.Jest programmer's ponoszą odpowiedzialność za zapewnienie, że dwie struktury mają ten sam układ danych).
Ponieważ członkowie struktury zarządzanej czasami zostaną ponownie posortowane w celach wydajności, to należy zastosować StructLayoutAttribute atrybut, aby wskazać, że struktury są kolejno ułożone.To jest również jawnie ustawiona struktura pakowania ustawienie, aby być takie same jak te stosowane przez naturalną strukturę. (Chociaż domyślnie Visual C++ korzysta strukturę 8-bajtowa opakowań dla obu zarządzanego kodu).
Następnie należy użyć polecenia DllImportAttribute Aby zadeklarować punkty wejścia, które odpowiadają wszelkich niezarządzanych funkcji, które akceptują strukturę, ale korzystania z zarządzanych wersji struktury w sygnatury funkcji, który jest kwestią sporną, jeśli używasz tej samej nazwy dla obu wersji struktury.
Teraz kodu zarządzanego można przekazywać zarządzanych wersję struktury do niezarządzanego funkcji tak, jakby były faktycznie zarządzanej funkcji.Struktury te mogą być przekazywane przez wartość lub przez odwołanie, jak pokazano w następującym przykładzie.
Przykład
Poniższy kod składa się z modułu zarządzane i niezarządzane.Moduł niezarządzanych to biblioteki DLL, która definiuje strukturę o nazwie lokalizacji i funkcji o nazwie GetDistance, która akceptuje dwa wystąpienia struktury lokalizacji.Drugi moduł to zarządzanej aplikacji wiersza polecenia, która importuje funkcji GetDistance, ale definiuje je w zarządzanych równowartości struktury lokalizacji, MLocation.W praktyce tę samą nazwę prawdopodobnie mogą być wykorzystane do obu wersji konstrukcji; jednak pod inną nazwą jest używany w tym miejscu do wykazania, że prototyp DllImport jest zdefiniowana w zarządzanych wersji.
Moduł zarządzany jest skompilowany z/CLR, ale/CLR: pure działa jak również.
Należy zauważyć, że żadna część biblioteki DLL jest narażony na kod zarządzany przy użyciu tradycyjnych # dyrektywy include.W rzeczywistości Biblioteka DLL jest dostępny w czasie wykonywania, więc nie zostaną wykryte problemy z funkcjami przywożone z elementu DllImport w czasie kompilacji.
// 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);
}
Zobacz też
Inne zasoby
Używanie jawnej funkcji PInvoke w języku C++ (atrybut DllImport)