54 votes

PInvoke pour la fonction C qui retourne char *

Je suis en train d'écrire du code C# qui appelle une méthode à partir d'une DLL non managée. Le prototype de la fonction dans la dll:

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

En C#, j'ai d'abord utilisé:

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

Il semble fonctionner sur la surface, mais je suis de corruption de la mémoire des erreurs lors de l'exécution. Je pense que je suis pointant à la mémoire de ce qui arrive à être correct, mais a déjà été libérée.

J'ai essayé d'utiliser un PInvoke code gen utilitaire appelé "P/Invoke Interop Assistant". Il m'a donné la sortie:

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

Est-ce correct? Si oui, comment puis-je convertir ce IntPtr à une chaîne de caractères en C#?

88voto

JaredPar Points 333733

Vous devez retourner ce qu'un IntPtr. De retour d'un Système.Type de chaîne à partir d'un PInvoke fonction nécessite le plus grand soin. Le CLR de transfert de la mémoire, de la représentation native dans la gestion de l'un. C'est un moyen facile et prévisible de l'opération.

Sauf que le problème est livré avec de quoi faire avec le natif de la mémoire qui a été renvoyé de foo(). Le CLR suppose les deux éléments suivants sur un PInvoke fonction qui renvoie directement le type de chaîne

  1. Le natif de la mémoire a besoin d'être libérée
  2. Le natif de la mémoire a été allouée avec CoTaskMemAlloc

Par conséquent, il sera maréchal de la chaîne, puis appelez CoTaskMemFree sur la mémoire natif blob. Sauf si vous avez réellement attribués à cette mémoire avec CoTaskMemAlloc ce sera, au mieux, provoquer un plantage de l'application.

Afin d'obtenir la sémantique correcte ici, vous devez retourner un IntPtr directement. Ensuite, utilisez le Maréchal.PtrToString* pour obtenir de gestion de la Chaîne de valeur. Vous pouvez toujours besoin de libérer de la mémoire natif, mais qui dépend de la mise en œuvre de foo.

33voto

Strelok Points 18453

Vous pouvez utiliser la méthode Marshal.PtrToStringAuto.

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

0voto

denis phillips Points 7349

Je serais intéressé de savoir quelle est la politique de l'API pour gérer la mémoire renvoyée par la fonction. Est-ce un pointeur vers un bloc de mémoire fixe qui est toujours là? Êtes-vous censé appeler une autre API pour libérer de la mémoire? Êtes-vous censé appeler vous-même une API WIN32 sur la mémoire retournée (par exemple, en appelant Marshal.FreeHGlobal ou Marshal.FreeCoTaskMem)?

Bien sûr, vous avez simplifié l'API pour la question, mais savez-vous quelle est la politique?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X