Condividi tramite


Procedura: Effettuare il marshalling di stringhe usando P/Invoke

Le funzioni native che accettano stringhe di tipo C possono essere chiamate usando il tipo stringa System::String CLR usando il supporto di .NET Framework Platform Invoke (P/Invoke). È consigliabile usare le funzionalità di interoperabilità C++ anziché P/Invoke, quando possibile. poiché P/Invoke fornisce segnalazione errori in fase di compilazione, non è indipendente dai tipi e può essere noioso da implementare. Se l'API non gestita viene inserita in un pacchetto come DLL e il codice sorgente non è disponibile, P/Invoke è l'unica opzione. In caso contrario, vedere Uso dell'interoperabilità C++ (P/Invoke implicito).

Le stringhe gestite e non gestite vengono disposte in modo diverso in memoria, quindi il passaggio di stringhe da funzioni gestite a funzioni non gestite richiede all'attributo MarshalAsAttribute di indicare al compilatore di inserire i meccanismi di conversione necessari per effettuare il marshalling dei dati stringa in modo corretto e sicuro.

Come per le funzioni che usano solo tipi di dati intrinseci, DllImportAttribute viene usato per dichiarare punti di ingresso gestiti nelle funzioni native. Le funzioni che passano stringhe possono usare un handle per il String tipo anziché definire questi punti di ingresso come stringhe di tipo C. L'uso di questo tipo richiede al compilatore di inserire il codice che esegue la conversione richiesta. Per ogni argomento della funzione in una funzione non gestita che accetta una stringa, usare l'attributo MarshalAsAttribute per indicare che il marshalling dell'oggetto String deve essere sottoposto a marshalling alla funzione nativa come stringa di tipo C.

Il gestore di marshalling esegue il wrapping della chiamata alla funzione non gestita in una routine wrapper nascosta. La routine wrapper aggiunge e copia la stringa gestita in una stringa allocata localmente nel contesto non gestito. La copia locale viene quindi passata alla funzione non gestita. Quando la funzione non gestita viene restituita, il wrapper elimina la risorsa. In alternativa, se si trovava nello stack, viene recuperato quando il wrapper esce dall'ambito. La funzione non gestita non è responsabile di questa memoria. Il codice non gestito crea ed elimina solo la memoria nell'heap configurata dal proprio CRT, quindi non esiste mai un problema con il marshaller usando una versione CRT diversa.

Se la funzione non gestita restituisce una stringa, come valore restituito o come parametro out, il gestore di marshalling lo copia in una nuova stringa gestita e quindi rilascia la memoria. Per altre informazioni, vedere Comportamento di marshalling predefinito e Marshalling dei dati con Platform Invoke.

Esempio

Il codice seguente è costituito da un modulo non gestito e da un modulo gestito. Il modulo non gestito è una DLL che definisce una funzione denominata TakesAString. TakesAStringaccetta una stringa stretta in stile C sotto forma di .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);
}

Il modulo gestito è un'applicazione della riga di comando che importa la TakesAString funzione, ma la definisce come accettando un oggetto gestito System.String anziché un oggetto char*. L'attributo MarshalAsAttribute viene usato per indicare come deve essere eseguito il marshalling della stringa gestita quando TakesAString viene chiamato.

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

Questa tecnica costruisce una copia della stringa nell'heap non gestito, quindi le modifiche apportate alla stringa dalla funzione nativa non verranno riflesse nella copia gestita della stringa.

Nessuna parte della DLL viene esposta al codice gestito dalla direttiva tradizionale #include . Infatti, la DLL è accessibile solo in fase di esecuzione, quindi i problemi nelle funzioni importate tramite non DllImport vengono rilevati in fase di compilazione.

Vedi anche

Uso esplicito di P/Invoke in C++ (DllImport attributo)