Postupy: Zařazení řetězců použitím PInvoke
Toto téma popisuje, jak mohou být nativní funkce, které přijímají řetězce ve stylu jazyka C, volány pomocí řetězce typu System::String použitím podpory volání nespravovaného kódu rozhraní .NET Framework. Programátoři jazyka Visual C++ místo toho používají funkce Interop jazaky C++ (pokud je to možné) protože P/Invoke poskytuje malé vykazování chyb při rychlosti kompilace, což 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, pak je P/Invoke jedinou možností , ale jinak, viz Použití interoperability C++ (implicitně PInvoke).
Spravované a nespravované řetězce jsou stanoveny odlišně v paměťi, takže předají řetězce ze spravovaných do nespravovaných funkcí, vyžadujících atribut MarshalAsAttribute na pokyn kompilátoru k vložení požadovaného převodního mechanismu pro správné a bezpečné zařazování řetězcových dat.
Jako s funkcemi, které používají pouze vnitřní datové typy, umožňuje DllImportAttribute deklarovat spravované vstupní body do nativních funkcí, ale--pro předávání řetězců--namísto definování těchto vstupních bodů jako přebírání řetězců ve stylu jazyka C, místo toho může být použit popisovač k typu String. To vyzve kompilátor k vložení kódu, který provede požadovaný převod. Pro každý argument funkce v nespravované funkci, která přebírá řetězec atributu MarshalAsAttribute, by měl být použit k označení, že objekt String by měl být zařazen do nativní funkce jako řetězec ve stylu jazyka C.
Příklad
Následující kód se skládá z nespravovaného a spravovaného modulu. Nespravovaný modul je knihovna DLL, která definuje funkci nazvanou TakesAString, přijímající řetězec ANSI ve stylu jazyka C ve formě char*. Spravovaný modul je aplikace příkazového řádku, která importuje funkci TakesAString, ale definuje ji jako přijetí spravovaného System.String namísto char*. Atribut MarshalAsAttribute slouží k určení, jak by měl být spravovaný řetězec zařazen při volání TakesAString.
Spravovaný modul je kompilován se /clr, ale /clr:pure pracuje stejně dobře.
// TraditionalDll2.cpp
// compile with: /LD /EHsc
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif
extern "C" {
TRADITIONALDLL_API void TakesAString(char*);
}
void TakesAString(char* p) {
printf_s("[unmanaged] %s\n", p);
}
// MarshalString.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
value struct TraditionalDLL
{
[DllImport("TraditionalDLL2.dll")]
static public void
TakesAString([MarshalAs(UnmanagedType::LPStr)]String^);
};
int main() {
String^ s = gcnew String("sample string");
Console::WriteLine("[managed] passing managed string to unmanaged function...");
TraditionalDLL::TakesAString(s);
Console::WriteLine("[managed] {0}", s);
}
Tento postup způsobí, že kopie řetězce bude konstruována na nespravované haldě, takže změny provedené pomocí nativní funkce řetězec se neprojeví ve spravované kopii řetězce.
Všimněte si, že žádná část knihovny DLL není zpřístupněna spravovanému kódu přes 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.
Viz také
Další zdroje
Použití explicitního PInvoke v jazyce C++ (atribut DllImport)