How to: Marshal strings using P/Invoke

Systemeigene Funktionen, die Zeichenfolgen im C-Stil akzeptieren, können mithilfe des CLR-Zeichenfolgentyps System::String mithilfe der Unterstützung von .NET Framework Platform Invoke (P/Invoke) aufgerufen werden. Wir empfehlen Ihnen, die C++-Interopfeatures anstelle von P/Invoke zu verwenden, wenn möglich. da P/Invoke wenig Kompilierzeitfehlerberichterstattung bereitstellt, nicht typsicher ist und nicht implementiert werden kann. Wenn die nicht verwaltete API als DLL verpackt ist und der Quellcode nicht verfügbar ist, ist P/Invoke die einzige Option. Andernfalls siehe Verwenden der C++-Interoperabilität (implizites P/Invoke).

Verwaltete und nicht verwaltete Zeichenfolgen werden im Arbeitsspeicher anders angeordnet, sodass das Übergeben von Zeichenfolgen von verwalteten an nicht verwaltete Funktionen das MarshalAsAttribute Attribut erfordert, dass der Compiler die erforderlichen Konvertierungsmechanismen zum ordnungsgemäßen und sicheren Marshallen der Zeichenfolgendaten einfügt.

Wie bei Funktionen, die nur systeminterne Datentypen verwenden, DllImportAttribute werden verwaltete Einstiegspunkte in die systemeigenen Funktionen deklariert. Funktionen, die Zeichenfolgen übergeben, können einen Handle für den String Typ verwenden, anstatt diese Einstiegspunkte als Zeichenfolgen im C-Stil zu definieren. Mit diesem Typ wird der Compiler aufgefordert, Code einzufügen, der die erforderliche Konvertierung ausführt. Verwenden Sie für jedes Funktionsargument in einer nicht verwalteten Funktion, die eine Zeichenfolge verwendet, das MarshalAsAttribute Attribut, um anzugeben, dass das String Objekt an die systemeigene Funktion als C-Formatzeichenfolge gemarstet werden soll.

Der Marshaler umschließt den Aufruf der nicht verwalteten Funktion in einer ausgeblendeten Wrapperroutine. Die Wrapperroutine pinsiert und kopiert die verwaltete Zeichenfolge in eine lokal zugeordnete Zeichenfolge im nicht verwalteten Kontext. Die lokale Kopie wird dann an die nicht verwaltete Funktion übergeben. Wenn die nicht verwaltete Funktion zurückgegeben wird, löscht der Wrapper die Ressource. Oder, wenn es sich auf dem Stapel befand, wird er wieder beansprucht, wenn der Wrapper den Gültigkeitsbereich überschreitet. Die nicht verwaltete Funktion ist für diesen Speicher nicht verantwortlich. Der nicht verwaltete Code erstellt und löscht nur Speicher im Heap, der von einem eigenen CRT eingerichtet wurde, sodass es nie ein Problem mit dem Marshaller mit einer anderen CRT-Version gibt.

Wenn Ihre nicht verwaltete Funktion eine Zeichenfolge zurückgibt, entweder als Rückgabewert oder als Ausgabeparameter, kopiert der Marshaler sie in eine neue verwaltete Zeichenfolge und gibt dann den Speicher frei. Weitere Informationen finden Sie unter Default Marshaling Behavior and Marshaling Data with Platform Invoke.

Beispiel

Der folgende Code besteht aus einem nicht verwalteten Modul und einem verwalteten Modul. Das nicht verwaltete Modul ist eine DLL, die eine Funktion definiert, die aufgerufen wird TakesAString. TakesAString akzeptiert eine schmale C-Zeichenfolge in Form eines 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);
}

Das verwaltete Modul ist eine Befehlszeilenanwendung, die die TakesAString Funktion importiert, definiert sie jedoch als verwaltete System.String anstelle einer char*. Das MarshalAsAttribute Attribut wird verwendet, um anzugeben, wie die verwaltete Zeichenfolge gemarstet werden soll, wenn TakesAString sie aufgerufen wird.

// 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);
}

Diese Technik erstellt eine Kopie der Zeichenfolge auf dem nicht verwalteten Heap, sodass Änderungen, die an der Zeichenfolge durch die systemeigene Funktion vorgenommen wurden, nicht in der verwalteten Kopie der Zeichenfolge widerzuspiegeln.

Für den verwalteten Code wird kein Teil der DLL durch die herkömmliche #include Direktive verfügbar gemacht. Tatsächlich wird zur Laufzeit nur auf die DLL zugegriffen, sodass Probleme in funktionen, die mithilfe der Verwendung DllImport importiert werden, zur Kompilierungszeit nicht erkannt werden.

Siehe auch

Verwenden expliziter P/Invoke in C++ (DllImport Attribut)