Libération de la mémoire non gérée à partir de C# avec le pointeur de
La question en quelques mots :
Comment libérer de la mémoire retourné à partir de DLL Native comme ItrPtr en code managé?
Détails :
Considérons le cas simple de la fonction prend deux paramètres en SORTIE, Le premier est un Pointeur de Référence pour le tableau d'octets et le second est la Référence Int .
La fonction permet d'allouer de la quantité d'octets selon certaines règles et de retourner le pointeur de la mémoire et de la taille d'octets et de la valeur de retour (1 succès et 0 pour non) .
Le code ci-dessous fonctionne très bien et je peux obtenir le tableau d'octets correctement et le nombre d'octets et la valeur de retour, mais quand j'ai essayer de libérer de la mémoire à l'aide du pointeur (IntPtr) - je obtenir de l'exception :
Windows a déclenché un point d'arrêt dans TestCppDllCall.exe.
Cela peut être dû à une corruption du tas, ce qui indique un bug dans TestCppDllCall.exe ou une Dll qu'il a chargé.
Cela peut être dû également à l'utilisateur en appuyant sur F12 pendant TestCppDllCall.exe a le focus.
La fenêtre de sortie peut avoir plus d'informations de diagnostic.
À mettre les choses au clair :
- Le prochain code C# fonctionner correctement avec d'autres DLL fonction ont la même signature et de libérer la mémoire fonctionne sans aucun problème .
- Toute modification à l'alinéa C) du code accepté si vous avez besoin de modifier l'allocation de la mémoire de la méthode ou de l'ajout de tout autre code .
- Toutes les fonctionnalités dont j'ai besoin est originaire DLL fonction accepte Deux paramètres par référence (tableau d'Octets et de type int En c# [IntPtr de tableau d'octets et int]) remplir avec certaines valeurs en fonction de certaines règles et de retourner le résultat (Succès ou Échec) .
CppDll.h
#ifdef CPPDLL_EXPORTS
#define CPPDLL_API __declspec(dllexport)
#else
#define CPPDLL_API __declspec(dllimport)
#endif
extern "C" CPPDLL_API int writeToBuffer(unsigned char *&myBuffer, int& mySize);
CppDll.cpp
#include "stdafx.h"
#include "CppDll.h"
extern "C" CPPDLL_API int writeToBuffer(unsigned char*& myBuffer, int& mySize)
{
mySize = 26;
unsigned char* pTemp = new unsigned char[26];
for(int i = 0; i < 26; i++)
{
pTemp[i] = 65 + i;
}
myBuffer = pTemp;
return 1;
}
De code C#:
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace TestCppDllCall
{
class Program
{
const string KERNEL32 = @"kernel32.dll";
const string _dllLocation = @"D:\CppDll\Bin\CppDll.dll";
const string funEntryPoint = @"writeToBuffer";
[DllImport(KERNEL32, SetLastError = true)]
public static extern IntPtr GetProcessHeap();
[DllImport(KERNEL32, SetLastError = true)]
public static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem);
[DllImport(_dllLocation, EntryPoint = funEntryPoint, CallingConvention = CallingConvention.Cdecl)]
public static extern int writeToBuffer(out IntPtr myBuffer, out int mySize);
static void Main(string[] args)
{
IntPtr byteArrayPointer = IntPtr.Zero;
int arraySize;
try
{
int retValue = writeToBuffer(out byteArrayPointer, out arraySize);
if (retValue == 1 && byteArrayPointer != IntPtr.Zero)
{
byte[] byteArrayBuffer = new byte[arraySize];
Marshal.Copy(byteArrayPointer, byteArrayBuffer, 0, byteArrayBuffer.Length);
string strMyBuffer = Encoding.Default.GetString(byteArrayBuffer);
Console.WriteLine("Return Value : {0}\r\nArray Size : {1}\r\nReturn String : {2}",
retValue, arraySize, strMyBuffer);
}
}
catch (Exception ex)
{
Console.WriteLine("Error calling DLL \r\n {0}", ex.Message);
}
finally
{
if (byteArrayPointer != IntPtr.Zero)
HeapFree(GetProcessHeap(), 0, byteArrayPointer);
}
Console.ReadKey();
}
}
}
Quand je debug ce code que j'ai mis un point de rupture dans la ligne (retour 1) et la valeur de la solution tampon a été :
myBuffer = 0x031b4fc0 "ABCDEFGHIJKLMNOPQRSTUVWXYZ««««««««î"
Et j'ai eu la même valeur dans le code C# lorsque la fonction de retour d'appel et de la valeur de a :
52121536
Le résultat que j'ai Obtenu le bon pointeur de la Mémoire et je suis en mesure d'obtenir le tableau d'octets de la valeur , de la façon de libérer de ces blocs de mémoire avec ce pointeur en C# ?
S'il vous plaît laissez-moi savoir si quelque chose n'est pas clair ou s'il y a un faute de frappe, je ne suis pas de langue maternelle anglaise .
OriginalL'auteur khaled | 2012-09-05
Vous devez vous connecter pour publier un commentaire.
Réponse courte: vous devez ajouter une méthode distincte dans la DLL qui libère de la mémoire pour vous.
Réponse longue: il y a différentes façons dont la mémoire peut être allouée à l'intérieur de votre mise en œuvre de DLL. La façon dont vous libérer de la mémoire, doit correspondre à la façon dont vous avez la mémoire allouée. Par exemple, la mémoire allouée avec
new[]
(avec les crochets) doit être libérée avecdelete[]
(par opposition àdelete
oufree
). C# ne pas fournir un mécanisme pour vous de le faire; vous devez envoyer le pointeur en C++.Je suis d'accord avec dasblinkenlight: le propre est sans doute pour ajouter un "libre" méthode de votre .dll. Mais Pierre Ritchie est correct, trop: il est certainement possible pour C# pour appeler (via interop) un correspondant "libre" (par exemple,
FreeCoTaskMem()
) cependant l' .dll affecté (e.gCoTaskMemAlloc()
). Une chose que vous pas à partir de C# est "supprimer", si l' .dll C++ "nouvelle". "malloc/free": OK. CoTaskMemAlloc/FreeCoTaskMem: aussi OK. "Nouveau/free": sans aucun doute PAS.Veuillez expliquer comment les invoquer en C# malloc/free?
Pierre Ritchie a expliqué comment - à l'aide de CoTaskMemAlloc() et le Maréchal.FreeCoTaskMem(), dans sa réponse ci-dessous (de retour en 2012!) Vous pouvez en lire plus ici: Regroupement des Données avec la Plate-forme Invoke. Remarque le commentaire "CoTaskMemAlloc doit être utilisé à la place de l'opérateur new, comme le code sur le côté géré vais appeler le Maréchal.FreeCoTaskMem pour libérer cette mémoire".
Je comprends mais quand dll est mallocing de la mémoire avec malloc puis Maréchal.FreeCoTaskMem devrait pas être utilisé pour libérer de la mémoire. J'ai l'impression que vous avez de trouver la façon de le faire appeler "libre" dans ce cas.
OriginalL'auteur dasblinkenlight
Si vous allouez de la mémoire dans le code natif, l'utilisation
CoTaskMemAlloc
et vous pouvez libérer le pointeur dans le code managé avecMaréchal.FreeCoTaskMem
.CoTaskMemAlloc
est décrit comme "le seul moyen de partager de la mémoire dans un COM-based application" (voir http://msdn.microsoft.com/en-us/library/windows/desktop/aa366533(v=vs. 85).aspx )si vous avez besoin d'utiliser la mémoire allouée avec
CoTaskMemAlloc
avec un modèle objet C++, vous pouvez utiliser placement de nouveaux pour initialiser la mémoire comme si lenew
opérateur a été utilisé. Par exemple:Ce n'alloue pas de mémoire avec
new
juste appelle le constructeur sur le pré-mémoire allouée.Appel
Marshal.FreeCoTaskMem
ne pas appeler le destructeur du type (qui n'est pas nécessaire si vous avez besoin de libérer de la mémoire); si vous avez besoin de faire plus que de libérer de la mémoire en appelant le destructeur, vous aurez à fournir une méthode native qui ne que et P/Invoke. En passant natif des instances de classe pour la gestion de code n'est pas pris en charge de toute façon.Si vous avez besoin d'allouer de la mémoire avec une autre API, vous aurez besoin de l'exposer dans le code managé par le biais de P/Invoke pour gratuit en code managé.
OriginalL'auteur Peter Ritchie
Non, ça ne peut pas marcher. Le segment de la poignée est mal, la CGT crée son propre tas avec HeapCreate(). Il est enterré dans le CRT de données, vous ne pouvez pas l'obtenir. Vous pourriez techniquement trouver la poignée de retour de GetProcessHeaps(), sauf que vous ne savez pas lequel c'est.
Le pointeur peut-être tort de trop, le CRT peut-être ajouté quelques informations supplémentaires à partir du pointeur retourné par HeapAlloc() pour stocker les données de débogage.
Vous devez exporter une fonction qui s'appelle delete[] pour libérer la mémoire tampon. Ou écrire en C++/CLI wrapper de sorte que vous pouvez utiliser delete[] dans l'emballage. Avec l'exigence que le code C++ et le wrapper utiliser le exacte même version de la DLL CRT (/MD nécessaire). Cela nécessite presque toujours, vous pouvez recompiler le code C++.
OriginalL'auteur Hans Passant