2009-09-30 10 views
1

J'utilise DllImport pour appeler la méthode dans la bibliothèque d'encapsuleur c de ma propre classe .net. Cette méthode dans c dll crée une variable chaîne et renvoie le pointeur de la chaîne.La mémoire libre n'efface pas le bloc de mémoire

Quelque chose comme ceci;

_declspec(dllexport) int ReturnString() 
{ 
char* retval = (char *) malloc(125); 
strcat(retval, "SOMETEXT"); 
strcat(retval, "SOMETEXT MORE"); 
return (int)retval; 
} 

Puis j'ai lu la chaîne en utilisant Marshall.PtrToStringAnsi (ptr). Après avoir obtenu une copie de la chaîne, j'appelle simplement une autre méthode c HeapDestroy qui est dans la librairie c wrapper qui appelle free (ptr).

Voici la question; Récemment, alors qu'il fonctionne comme un charme, j'ai commencé à obtenir l'exception «Tentative de lecture ou d'écriture de zone de mémoire protégée». Après une analyse plus approfondie, j'ai compris, je crois, bien que j'appelle méthode libre pour ce pointeur, la valeur du pointeur n'est pas effacée, et ceci remplit le tas sans surveillance et fait que mon processus de travail iis lance cette exception. En passant, c'est un projet de site Web qui appelle cette méthode dans la bibliothèque c.

Pourriez-vous m'aider à ce sujet?

Bien sûr, voici le code C#;

[DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 
    private extern static int ReturnString(); 

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 
    private extern static void HeapDestroy(int ptr); 

    public static string GetString() 
    { 
     try 
     { 

      int i = ReturnString(); 
      string result = String.Empty; 
      if (i > 0) 
      { 
       IntPtr ptr = new IntPtr(i); 
       result = Marshal.PtrToStringAnsi(ptr); 
       HeapDestroy(i); 
      } 

      return result; 
     } 
     catch (Exception e) 
     { 
      return String.Empty; 
     } 
    } 
+0

Pourriez-vous s'il vous plaît poster le code C# que vous utilisez? –

+0

J'ai ajouté le code C#. Merci –

Répondre

7

Quel peut être le problème est le code C sous-jacent. Vous n'ajoutez pas de terminateur NULL à la chaîne sur laquelle repose strcat (ou recherchez un retour NULL à partir de malloc). Il est facile d'obtenir de la mémoire corrompue dans ce scénario. Vous pouvez résoudre ce problème en procédant comme suit.

retval[0] = '\0'; 
strcat(retval, "SOMETEXT"); 

Le problème réside également dans le fait que vous jouez des tours sur le système. Il est préférable de l'écrire correctement et de laisser le système fonctionner correctement. La première étape consiste à corriger le code natif pour renvoyer correctement la chaîne. Une chose que vous devez prendre en compte est que seuls certains types de mémoire peuvent être libérés nativement par le CLR (allocations HGlobal et CoTask). Changeons donc la signature de la fonction pour retourner un char* et utiliser un allocateur différent.

_declspec(dllexport) char* ReturnString() 
{ 
char* retval = (char *) CoTaskMemAlloc(125); 
retval[0] = '\0'; 
strcat(retval, "SOMETEXT"); 
strcat(retval, "SOMETEXT MORE"); 
return retval; 
} 

Ensuite, vous pouvez utiliser la suite de la signature C# et libérer le IntPtr avec Marshal.FreeCoTaskMem.

[DllImport("SomeDll.dll")] 
public static extern IntPtr ReturnString(); 

Encore mieux. Lors du rassemblement, si le CLR pense qu'il a besoin de libérer de la mémoire, il utilisera FreeCoTaskMem pour le faire. Ceci est généralement pertinent pour les retours de chaînes. Depuis que vous avez alloué la mémoire avec CoTaskMemAlloc vous pouvez vous enregistrer le + marshalling libérant ainsi les étapes et procédez comme suit

[DllImport("SomeDll.dll", CharSet=Ansi)] 
public static extern String ReturnString(); 
+0

Je vais essayer ça! Cela semble raisonnable. –

+0

JaredPar, votre réponse est très utile. Mais une autre question pour votre exemple, quel est l'équivalent de CoTaskMemAlloc dans ansi-c, existe-t-il dans la bibliothèque standard? –

+1

@Emrah, il n'y a pas d'équivalent dans la bibliothèque standard ansi-c. Si vous êtes limité à ansi-c, vous aurez probablement besoin d'écrire une couche PInvoke rapide gratuitement et de passer l'IntPtr ici. – JaredPar

1

mémoire ne Libérant clair, il libère juste en sorte qu'il peut être réutilisé. Certains debug va écrire sur la mémoire pour vous le rendre plus facile de trouver des problèmes avec des valeurs telles que 0xBAADFOOD

Les appelants doivent allouer de la mémoire, ne passera jamais en arrière mémoire allouée:

_declspec(dllexport) int ReturnString(char*buffer, int bufferSize) 
{ 
    if (bufferSize < 125) { 
     return 125; 
    } else { 
     strcat(buffer, "SOMETEXT"); 
     strcat(buffer, "SOMETEXT MORE"); 
     return 0; 
    } 
} 
0

Bien que la mémoire est allouée par la DLL dans le même tas que votre application, il PEUT utiliser un gestionnaire de mémoire différent, en fonction de la bibliothèque avec laquelle il était lié. Vous devez soit vous assurer que vous utilisez la même bibliothèque exacte, ou ajouter du code pour libérer la mémoire que la DLL alloue, dans le code DLL lui-même.

+0

Oui, c'est pourquoi la méthode HeapDestroy ajoutée qui contient une simple méthode gratuite (ptr) à appeler. –

+0

Cool. J'ai mal interprété votre question. Je pensais que vous aviez écrit un wrapper autour de la DLL, donc la DLL faisait l'alloc pendant que votre code wrapper faisait le gratuit. – justinhj

+0

Je recommanderais d'utiliser des opérations plus sûres qui fonctionnent sur des chaînes limitées. Mettez votre buffersize dans une constante et passez-la aux fonctions qui agissent sur la chaîne de caractères allouée. Utilisez strncat et ainsi de suite. – justinhj

Questions connexes