2008-12-15 19 views
41

Estoy tratando de escribir algún código C# que invoque un método desde una DLL no administrada. El prototipo de la función en la DLL es:PInvoke para la función C que devuelve char *

extern "C" __declspec(dllexport) char *foo(void); 

En C#, utilizó por primera vez:

[DllImport(_dllLocation)] 
public static extern string foo(); 

Parece que funciona en la superficie, pero estoy recibiendo errores de corrupción de memoria en tiempo de ejecución. Creo que estoy apuntando a la memoria que pasa a ser correcta, pero que ya ha sido liberada.

Intenté usar una utilidad de código gen PInvoke llamada "P/Invoke Interop Assistant". Me dio la salida:

[System.Runtime.InteropServices.DLLImportAttribute(_dllLocation, EntryPoint = "foo")] 
public static extern System.IntPtr foo(); 

¿Es esto correcto? Si es así, ¿cómo convierto este IntPtr a una cadena en C#?

Respuesta

67

Debe devolver esto como un IntPtr. La devolución de un tipo System.String desde una función PInvoke requiere gran cuidado. El CLR debe transferir la memoria de la representación nativa a la manejada. Esta es una operación fácil y predecible.

El problema viene con qué hacer con la memoria nativa devuelta desde foo(). El CLR asume los siguientes dos artículos acerca de una función PInvoke que devuelve directamente el tipo de cadena necesita ser liberado

  • la memoria nativa se asignó con CoTaskMemAlloc

    1. la memoria nativa

    Por lo tanto, será reunir la cadena y luego llame a CoTaskMemFree en el blob de memoria nativo. A menos que realmente haya asignado esta memoria con CoTaskMemAlloc, esto en el mejor de los casos causará un bloqueo en su aplicación.

    Para obtener la semántica correcta aquí debe devolver un IntPtr directamente. Luego use Marshal.PtrToString * para obtener un valor de cadena administrado. Es posible que aún necesite liberar la memoria nativa, pero eso dependerá de la implementación de foo.

  • 19

    Puede usar el método Marshal.PtrToStringAuto.

    IntPtr ptr = foo(); 
    string str = Marshal.PtrToStringAuto(ptr); 
    
    +1

    Esto funcionó para mí. El único cambio que tuve que hacer fue 'PtrToStringAuto' para' PtrToStringAnsi'; de lo contrario, obtuve algunos caracteres chinos – 3vts

    Cuestiones relacionadas