2011-03-15 18 views
25

Estoy utilizando Pinvoke para la interoperabilidad entre el código nativo (C++) y el código administrado (C#). Lo que quiero lograr es obtener algo de texto del código nativo en mi código administrado. Para esto, intento muchas cosas, por ejemplo, pasar string/stringbuilder por ref, usar [IN] y [OUT], Marshaling to LPSTR, devolver string desde la función, etc. pero nada funciona en mi caso. Cualquier ayuda con algún código pequeño sería muy apreciada.Cómo devolver el texto del código nativo (C++)

Respuesta

42

que lo haría con un BSTR ya que significa que usted no tiene que poner en nativa dos veces por cadena, una vez para obtener la longitud y luego una vez para obtener el contenido.

Con un BSTR, el marshaller se encargará de desasignar el BSTR con el administrador de memoria correcto para que pueda pasarlo con seguridad de su código C++.

C++

#include <comutil.h> 
BSTR GetSomeText() 
{ 
    return ::SysAllocString(L"Greetings from the native world!"); 
} 

C#

[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)] 
[return: MarshalAs(UnmanagedType.BStr)] 
private static extern string GetSomeText(); 

Hay un inconveniente menor de la BSTR, a saber, que lleva una carga útil UTF-16, pero los datos de origen también puede ser char* .

Para superar esto, puede terminar la conversión de char* a BSTR así:

BSTR ANSItoBSTR(const char* input) 
{ 
    BSTR result = NULL; 
    int lenA = lstrlenA(input); 
    int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0); 
    if (lenW > 0) 
    { 
     result = ::SysAllocStringLen(0, lenW); 
     ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW); 
    } 
    return result; 
} 

Esa es la más difícil del camino, y ahora es fácil añadir otros envoltorios para convertir a BSTR de LPWSTR , std::string, etc. std::wstring

+0

@rturrado Muchas gracias por sus correcciones en la edición. –

+0

la respuesta es muy útil, por lo que se lo merece. Por cierto, me estoy dando cuenta de que 'bien puede chatear *' le falta un 'ser'. – rturrado

+0

Puede pasar '-1' como cuarto argumento a' MultiByteToWideChar' para que la función calcule la longitud de la cadena automáticamente; evita la necesidad de llamar a 'lstrlenA' usted mismo. –

3

Here es un tema sobre el que se discutió la clasificación de cadenas.

necesidad de para marcar el parámetro con el atributo

[MarshalAs(UnmanagedType.LPSTR)] 
4

Aquí es un ejemplo de hacer esto a través de C#. Estoy llamando a la función nativa GetWindowText a través de C# invocando. GetWindowText devuelve el título de la ventana cuyo handle se le pasa.

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); 
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    static extern int GetWindowTextLength(IntPtr hWnd); 

    public static string GetText(IntPtr hWnd) 
    { 
     // Allocate correct string length first 
     int length = GetWindowTextLength(hWnd); 
     StringBuilder sb = new StringBuilder(length + 1); 
     GetWindowText(hWnd, sb, sb.Capacity); 
     return sb.ToString(); 
    }   

    private void button1_Click(object sender, EventArgs e) 
    { 
     string str = GetText(this.Handle); 
    } 
0

Si usted está volviendo un char *, y no quiero tener que modificar el código C/C++ para asignar memoria para el valor de retorno (o no se puede modificar ese código), entonces puedes cambiar tu Prototipo de función Ctern extern para devolver un IntPtr, y haz los cálculos tú mismo.

Por ejemplo, aquí es un fragmento de la interoperabilidad que escribí para Pocketsphinx:

[DllImport("sphinxbase.dll", CallingConvention = CallingConvention.Cdecl)] 
private static extern IntPtr /* char const * */ jsgf_grammar_name(IntPtr /* jsgf_t * */ jsgf); 

Y aquí está el acceso get para la clase C# JsgfGrammar. m_pU es un IntPtr que apunta al objeto raw jsgf_t.

public string Name 
{ 
    get 
    { 
     IntPtr pU = jsgf_grammar_name(m_pU); 
     if (pU == IntPtr.Zero) 
      strName = null; 
     else 
      strName = Marshal.PtrToStringAnsi(pU); 
     return strName; 
    } 
} 

Modificar este ejemplo para otros formatos de cadena (por ejemplo, Unicode) debe ser trivial.

Cuestiones relacionadas