2011-05-27 11 views
5

He leído otras preguntas similares sobre esto, pero no resuelven este problema en particular. Tengo una antigua biblioteca C con una función touppercase (como ejemplo). Esto toma un char * y devuelve un char *. Sin embargo, el puntero devuelto es un puntero a la misma cadena (no me preguntes si no lo escribí).Llamar a una función C DLL Con char * Parámetros con P/Invoke

La función tiene el siguiente aspecto:

__declspec(dllexport) 
char * __cdecl touppercase(char *ps_source) 
{ 
    char *ps_buffer = NULL; 

    assert (ps_source != NULL); 

    ps_buffer = ps_source; 
    while (*ps_buffer != '\0') 
    { 
     *ps_buffer = toupper(*ps_buffer); 
     ps_buffer++; 
    } 
*ps_buffer = '\0'; 
    return (ps_source); 
} 

El código C# para declarar esto se parece a:

[DllImport("mydll.dll", EntryPoint = "touppercase", 
       CharSet = CharSet.Ansi, ExactSpelling = true, 
       CallingConvention = CallingConvention.Cdecl)] 
    private static extern System.IntPtr touppercase(string postData); 

La llamada a este en mi aplicación parece

 string sTest2 = Marshal.PtrToStringAnsi(to_uppercase(sTest)); 

Sin embargo sTest2 acaba siendo una cadena aleatoria.

Agregué una función de prueba al mismo dll con los mismos parámetros pero esto asigna memoria localmente y copia la cadena. Esto funciona bien ¿Por qué la versión original ahora funciona?

Nota: La actualización de las bibliotecas dll no es una opción.

+0

¿Qué sucede con sTest después de una llamada a esta función? ¿cambia? –

+0

¿Por qué declaras la convención de llamadas? Por defecto, C# usa la convención de llamadas en lugar de la convención estándar (C/C++). Ya cambias la convención en tu biblioteca C. Solo tendrías que cambiar la convención si el código C no fue declarado como algo diferente al predeterminado. –

+1

@ramhound: la especificación explícita de la convención de llamadas de las funciones exportadas es más segura. No desea que las exportaciones se vean afectadas por '/ Gr' o'/Gz'. –

Respuesta

10

La función está modificando la referencia de lo que debe utilizar un StringBuilder:

[DllImport("dll.dll", EntryPoint="touppercase", 
CharSet = CharSet.Ansi, ExactSpelling = true, 
CallingConvention = CallingConvention.Cdecl)] 
private static extern System.IntPtr touppercase(StringBuilder postData); 

llamarlo de esta manera:

StringBuilder sTest = new StringBuilder("abs"); 
    touppercase(sTest); 
    string result = sTest.ToString(); 

Para la explicación del puntero de retorno -> Ben Voigt

+0

Eso funcionó de maravilla. Muchas gracias. – Jonnster

3

Como correctamente observó, el tipo de devolución es un puntero a la misma cadena. Y la cadena provista a la función es un buffer temporal usado por .NET para Unicode-> conversión ANSI, desasignado antes de que P/Invoke regrese. Por lo tanto, es un puntero salvaje que debe ignorar (lo mejor es usar el tipo de retorno C# void).

También recomendaría usar StringBuilder para el parámetro en lugar de String, esto le permite a .NET saber que la función cambiará el parámetro pasado. El contenido del StringBuilder será modificado por la función, así es como obtienes los resultados sin un valor de retorno.

+0

Muchas gracias. – Jonnster

Cuestiones relacionadas