Compartir a través de


Cómo: serializar cadenas mediante P/Invoke

Se puede llamar a las funciones nativas que aceptan cadenas de estilo C mediante el tipo de cadena CLR System::String mediante la compatibilidad de invocación de plataforma de .NET Framework (P/Invoke). Le recomendamos que use las características de interoperabilidad de C++ en lugar de P/Invoke cuando sea posible. como P/Invoke proporciona pocos informes de errores en tiempo de compilación, no se considera con seguridad de tipos y puede ser tedioso implementarlo. Si la API no administrada se empaqueta como una DLL y el código fuente no está disponible, P/Invoke es la única opción. De lo contrario, vea Utilizar la interoperabilidad de C++ (PInvoke implícito).

Las cadenas administradas y no administradas se colocan de forma diferente en la memoria, por lo que pasar cadenas de funciones administradas a no administradas requiere el atributo MarshalAsAttribute para indicar al compilador que inserte los mecanismos de conversión necesarios para serializar los datos de cadena de forma correcta y segura.

Al igual que con las funciones que solo usan tipos de datos intrínsecos, DllImportAttribute se usa para declarar puntos de entrada administrados en las funciones nativas. Las funciones que pasan cadenas pueden usar un identificador para el tipo String en lugar de definir estos puntos de entrada como cadenas de estilo C. El uso de este tipo solicita al compilador que inserte código que realice la conversión necesaria. Para cada argumento de función de una función no administrada que toma una cadena, use el atributo MarshalAsAttribute para indicar que el objeto String se debe serializar a la función nativa como una cadena de estilo C.

El serializador encapsula la llamada a la función no administrada en una rutina contenedora oculta. La rutina contenedora ancla y copia la cadena administrada en una cadena asignada localmente en el contexto no administrado. Después, la copia local se pasa a la función no administrada. Cuando la función no administrada vuelve, el contenedor elimina el recurso. O, si estaba en la pila, se reclama cuando el contenedor sale del ámbito. La función no administrada no es responsable de esta memoria. El código no administrado solo crea y elimina memoria en el montón configurado por su propio CRT, por lo que nunca hay un problema con el herramienta de referencia mediante una versión de CRT diferente.

Si la función no administrada devuelve una cadena, ya sea como un valor devuelto o un parámetro out, el serializador la copia en una nueva cadena administrada y, después, libera la memoria. Para obtener más información, vea Comportamiento de serialización predeterminado y Marshaling Data with Platform Invoke (Serialización de datos con la invocación de plataforma).

Ejemplo

El código siguiente consta de un módulo no administrado y un módulo administrado. El módulo no administrado es una DLL que define una función denominada TakesAString. TakesAString acepta una cadena estrecha de estilo C en forma de 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);
}

El módulo administrado es una aplicación de línea de comandos que importa la función TakesAString, pero lo define como una aplicación administrada System.String en lugar de char*. El atributo MarshalAsAttribute se usa para indicar cómo se debe serializar la cadena administrada cuando se llama a TakesAString.

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

Esta técnica construye una copia de la cadena en el montón no administrado, por lo que los cambios realizados en la cadena por la función nativa no se reflejan en la copia administrada de la cadena.

La directiva tradicional #include no expone ninguna parte del archivo DLL al código administrado. De hecho, solo se tiene acceso al archivo DLL en runtime, por lo que los problemas en las funciones importadas mediante DllImport no se detectan en tiempo de compilación.

Consulte también

Utilizar un elemento PInvoke explícito en C++ (Atributo DllImport)