2017-08-03 2 views
0

Je suis en train de P/Invoke une API C++ retour d'un char * qui est une chaîne en C#. Je sais que cela doit être pris par IntPtr puis convertir le pointeur sur chaîne en utilisant la Marshaler comme si,Problème avec l'API avec char * retour étant pris par IntPtr dans P/Invoke

C++ API

char* WINAPI MY_API(const char *basebuf, char *strbuf) 
{ 
    return targetfunction(basebuf, strbuf); 
} 

C# P/Invoke

[DllImport("myDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] 
internal static extern IntPtr MY_API([MarshalAs(UnmanagedType.LPStr)] string basebuf, 
            [MarshalAs(UnmanagedType.LPArray), Out] byte[] strbuf); 

C# Wrapper

public string MY_API_WRAPPER(string basebuf, out string strbuf) 
{ 
    byte[] strbufTemp = new byte[256]; 

    IntPtr ret = MY_API(basebuf, strbufTemp); 
    string retstr = Marshal.PtrToStringAnsi(ret); 
    strbuf = MyDataConverter.ByteToString(strbufTemp); 

    return retstr; 
} 

La chaîne de paramètres est sur OK. mais la chaîne retournée (convertie depuis IntPtr) est garbage.

Je peux modifier le C++ API pour affecter le char * à la tâche mémoire libre puis dans le C# Wrapper, mais en changeant le code natif était pas une option. aussi il y a un risque de fuite de mémoire si l'API était utilisée sans le wrapper.

je me demandais ce qui est arrivé au pointeur lorsque l'appel d'API est terminée?

+0

Comment est la valeur de retour allouée? –

+0

@DavidHeffernan La valeur de retour de "targetfunction" est un pointeur vers les éléments de basebuf. quelque chose comme ça (char * ret = (char *) & basebuf [0]) –

+1

Cela ne va pas fonctionner alors. C'est un tampon temporaire géré par le marshaler pinvoke. Non valide après le retour de la fonction non managée. Vous aurez besoin de marshaler basebuf manuellement. –

Répondre

0

Merci à David, j'ai pu résoudre mon problème. Étant donné que la valeur de retour de la fonction est un pointeur sur le paramètre basebuf, elle acquiert une valeur garbage lorsque la fonction renvoie.

Le basebuf devrait être un IntPtr pas une chaîne pour le retour IntPtr pour obtenir la valeur correcte après l'appel. Donc, il devrait être P/Invoqué comme ça.

C# P/Invoke

[DllImport("myDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] 
internal static extern IntPtr MY_API(IntPtr basebuf, 
            [MarshalAs(UnmanagedType.LPArray), Out] byte[] strbuf); 

C# Wrapper

public string MY_API_WRAPPER(string basebuf, out string strbuf) 
{ 
    byte[] strbufTemp = new byte[256]; 
    IntPtr basebufPtr = Marshal.StringtoHGlobalAnsi(basebuf) 
    IntPtr ret = MY_API(basebufPtr , strbufTemp); 
    string retstr = Marshal.PtrToStringAnsi(ret); 
    strbuf = MyDataConverter.ByteToString(strbufTemp); 
    Marshal.FreeHGlobal(basebufPtr); 
    return retstr; 
}