Postupy: Zařazování řetězců pomocí volání nespravovaného kódu
Nativní funkce, které přijímají řetězce ve stylu jazyka C, lze volat pomocí typu System::String
řetězce CLR pomocí podpory volání platformy .NET Framework (P/Invoke). Doporučujeme, abyste místo volání nespravovaného kódu používali funkce spolupráce C++, pokud je to možné. protože volání nespravovaného kódu poskytuje zasílání zpráv o chybách v době kompilace málo, není typově bezpečné a nelze je implementovat. Pokud je nespravované rozhraní API zabalené jako knihovna DLL a zdrojový kód není k dispozici, je jedinou možností volání nespravovaného kódu. V opačném případě se podívejte na použití zprostředkovatele komunikace C++ (implicitní volání nespravovaného kódu).
Spravované a nespravované řetězce jsou v paměti rozloženy odlišně, takže předávání řetězců ze spravovaných do nespravovaných funkcí vyžaduje MarshalAsAttribute atribut, aby kompilátoru dal pokyn, aby vložil požadované mechanismy převodu pro správné a bezpečné zařazování řetězcových dat.
Stejně jako u funkcí, které používají pouze vnitřní datové typy, DllImportAttribute slouží k deklaraci spravovaných vstupních bodů do nativních funkcí. Funkce, které předávají řetězce, mohou místo definování těchto vstupních bodů použít popisovač String typu C. Použití tohoto typu vyzve kompilátor k vložení kódu, který provádí požadovaný převod. Pro každý argument funkce v nespravované funkci, která přebírá řetězec, použijte MarshalAsAttribute atribut k označení, že String
objekt by měl být zařazen do nativní funkce jako řetězec ve stylu C.
Zařazovač zabalí volání nespravované funkce do skryté rutiny obálky. Rutina obálky připne a zkopíruje spravovaný řetězec do místně přiděleného řetězce v nespravovaném kontextu. Místní kopie se pak předá nespravované funkci. Když se nespravovaná funkce vrátí, obálka prostředek odstraní. Nebo pokud byl na zásobníku, je uvolněný, když obálka zmizí z rozsahu. Nespravovaná funkce není zodpovědná za tuto paměť. Nespravovaný kód pouze vytvoří a odstraní paměť v haldě nastavené vlastním CRT, takže nikdy není problém s marshallerem pomocí jiné verze CRT.
Pokud nespravovaná funkce vrátí řetězec, buď jako návratovou hodnotu, nebo výstupní parametr, zařazovač ho zkopíruje do nového spravovaného řetězce a pak uvolní paměť. Další informace najdete v tématu Výchozí chování zařazování a zařazování dat s voláním platformy.
Příklad
Následující kód se skládá z nespravovaného modulu a spravovaného modulu. Nespravovaný modul je knihovna DLL, která definuje funkci s názvem TakesAString
. TakesAString
přijímá úzký řetězec ve stylu jazyka C ve formě char*
.
// 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);
}
Spravovaný modul je aplikace příkazového řádku, která importuje TakesAString
funkci, ale definuje ji jako spravovanou System.String
místo char*
. Atribut MarshalAsAttribute se používá k označení, jak se má spravovaný řetězec zařazovat při TakesAString
zavolání.
// 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);
}
Tato technika vytvoří kopii řetězce na nespravované haldě, takže změny provedené v řetězci nativní funkcí se neprojeví ve spravované kopii řetězce.
Tradiční direktiva #include
neobsahuje žádnou část knihovny DLL. Knihovna DLL je ve skutečnosti přístupná pouze za běhu, takže problémy s funkcemi importovanými pomocí DllImport
nejsou v době kompilace zjištěny.
Viz také
Použití explicitního volání nespravovaného kódu v jazyce C++ (DllImport
atribut)