La pregunta en palabras cortas es: ¿Cómo liberar la memoria devuelta desde el DLL nativo como ItrPtr en el código administrado?Liberar la memoria no administrada del C# administrado con el puntero del mismo
Detalles: Supongamos que tenemos una función simple toma dos parámetros como SALIDA, El primero es el Puntero de referencia a la matriz de bytes y el segundo es Referencia Int. La función asignará la cantidad de bytes según algunas reglas y devolverá el puntero de la memoria y el tamaño de los bytes y el valor de retorno (1 para el éxito y 0 para el error).
El código siguiente funciona bien y puedo obtener la matriz de bytes correctamente y el recuento de bytes y el valor de retorno, pero cuando intento para liberar la memoria utilizando el puntero (IntPtr) me sale excepción:
Windows ha desencadenado un punto de interrupción en TestCppDllCall.exe.
Esto puede deberse a una corrupción del montón, que indica un error en TestCppDllCall.exe o cualquiera de las DLL que ha cargado.
Esto también puede deberse a que el usuario presiona F12 mientras TestCppDllCall.exe tiene foco.
La ventana de salida puede tener más información de diagnóstico.
Para dejar las cosas claras:
El siguiente código C# funciona correctamente con otra función DLL tienen la misma firma y liberar la memoria funciona sin ningún problema.
Se acepta cualquier modificación en el código (C) si necesita cambiar el método de memoria de asignación o agregar cualquier otro código.
Toda la funcionalidad que necesito es la función nativa DLL aceptar dos parámetros por referencia (matriz de bytes y int, en C# [IntPtr de byte array y int]) llenarlos con algunos valores basados en algunas reglas y devolver el resultado de la función (Éxito o fracaso).
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;
}
C# código:
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();
}
}
}
Cuando depurar este código i conjunto de puntos de ruptura en la línea (retorno 1) y el valor del tampón fue:
myBuffer = 0x031b4fc0 "ABCDEFGHIJKLMNOPQRSTUVWXYZ««««««««î"
Y obtuve el mismo valor en el código C# cuando la función devolvió la llamada y el valor fue:
52121536
El resultado Obtuve el puntero de Memoria correcto y puedo obtener el valor del conjunto de bytes, ¿cómo liberar estos bloques de memoria con este puntero en C#?
Háganme saber si algo no está claro o si hay algún error tipográfico, no soy hablante nativo de inglés.
Gracias por su rápida respuesta. Esperaba que haya una manera de liberar memoria no administrada en C# directamente. – khaled
Estoy de acuerdo con dasblinkenlight: la forma * más limpia * es sin duda agregar un método "libre" a su .dll. Pero Peter Ritchie también está en lo cierto: ciertamente es posible que C# invoque (a través de la interoperabilidad) un "libre" correspondiente (por ejemplo, 'FreeCoTaskMem()') a sin embargo el .dll asignado (por ejemplo 'CoTaskMemAlloc()'). Una cosa que * no * se puede hacer desde C# es "eliminar", si el .dll hace un C++ "nuevo". "malloc/free": OK. CoTaskMemAlloc/FreeCoTaskMem: también está bien. "Nuevo/gratis": definitivamente * NO *. – paulsm4
@ paulsm4 Por favor, explique cómo invocar en C# malloc/free? – awattar