2010-02-01 20 views
12

Probé cosas diferentes, pero me estoy poniendo loco de interoperabilidad.Paso C# cadena de C++ y C++ pasar resultado (cadena, char * .. lo que sea) a C#

(aquí la palabra cadena no se refiere a un tipo variable pero "una colección de caracteres"): Tengo una función C++ no administrada, definida en un dll, que estoy tratando de acceder desde C#, esta función tiene un parámetro de cadena y un valor de retorno de cadena como este:

string myFunction(string inputString) 
{ 
} 

¿Cuál debería ser la cadena en el lado de C++? y C# uno? y qué parámetros necesitan DllImport para esto?

Respuesta

17

Lo que he encontrado para trabajar mejor es ser más explícito acerca de lo que está pasando aquí. Tener una cadena como tipo de retorno probablemente no se recomienda en esta situación.

Un enfoque común es hacer que el lado de C++ pase el búfer y el tamaño del búfer. Si no es lo suficientemente grande para lo que GetString tiene que poner en él, la variable bufferSize se modifica para indicar cuál sería el tamaño apropiado. El programa de llamada (C#) aumentaría el tamaño del búfer al tamaño apropiado.

Si esta es su función DLL exportado (C++):

extern "C" __declspec void GetString(char* buffer, int* bufferSize); 

Matching C# sería el siguiente:

void GetString(StringBuilder buffer, ref int bufferSize); 

Así que para usar esto en C# usted entonces hacer algo como lo siguiente :

int bufferSize = 512; 
StringBuilder buffer = new StringBuilder(bufferSize); 
GetString(buffer, ref bufferSize); 
+1

Este es el enfoque correcto. Tenga en cuenta que no tiene que pasar el tamaño del búfer por referencia. El C/C++ debe terminar la cadena, buffer.ToString() produce el valor de retorno. –

+0

Gracias a todos por las respuestas. Leí en alguna parte que StringBuilder se utiliza para la cadena de salida, pero también necesito pasar a la función C++ una cadena de entrada ... ¿qué tipo debo usar? En el lado de C++, he insertado la cadena de caracteres char * y C#, pero no funciona. Finalmente, ¿puedo definir la salida "cadena" (lado C++) como un const char *? O debo tener un char *? – Smjert

+0

@Smjert: Usar un const char * es una buena forma de hacerlo si no lo está cambiando. Esto debería corresponder perfectamente con una cadena en el lado C#. ¿Qué tipo de problemas estás viendo? –

2

La única buena manera que conozco de hacer esto es escribir una clase contenedora .NET C++ usando Managed C++ Extensions, y dentro del objeto .NET C++ llamar a su código C++ nativo. Hay funciones en las extensiones administradas para convertir un System.String a un char * o cualquier otro tipo de cadena no administrada.

Básicamente, usted crea una clase .NET usando C++ y la expone de un ensamblado, y internamente a ese ensamblado puede llamar a su código C++ nativo. La otra forma es agregar una función C pura a su código C++ utilizando P/Invoke y luego llamar a su código C desde C# y hacer que su función C llame a su código C++. Esto funcionará, pero tiendo a tratar de usar código administrado tanto como sea posible.

2

El mayor problema con pasar cadenas de C++ a C# es la asignación de memoria. El GC debería poder saber cómo limpiar la memoria asignada para esta cadena. Como C# tiene una amplia compatibilidad de interm de COm, sí sabe acerca de los BSTR de COM y cómo asignarlos y desasignarlos. Así, la forma más fácil de hacer esto sería utilizar BSTR en el lado del C++ y string en el lado C#.

Nota: el uso de BSTR no implica que su función deba exponerse a través de COM.

2

El valor de retorno de "cadena" es el problema. El Marshaller P/Invoke llamará a CoTaskMemFree() en el puntero que devuelva. Eso no funcionará bien a menos que use CoTaskMemAlloc() en su código C/C++ para asignar el búfer de cadena. Lo cual es algo bastante inusual de hacer.

La mejor solución es permitir que la persona que llama de su código pase un puntero a un búfer y la longitud del búfer a usted como argumentos. De esta forma, toda la asignación de memoria ocurre por un lado. Scott te mostró cómo hacer esto.

1

I tenía que convertir un # cadena Unicode C a una representación multibyte el fin de convertir a char * en C++ (esto es solución parcial de una forma)

I encontraron la siguiente muy útil

string st; 
IntPtr stPtr = Marshal.StringToHGlobalAnsi(st); 
// Do your thing in C++ 

Marshal.FreeHGlobal(stPtr); 

Esto puede ser ineficiente y no en C# forma, soy nuevo en C#, espero que esto ayude