Pytanie w skrócie to: Jak zwolnić pamięć zwróconą przez Native DLL jako ItrPtr w zarządzanym kodzie?Zwolnij pamięć niezarządzaną z zarządzanego C# za pomocą wskaźnika
Szczegóły: Szczegóły: Założenie, że mamy funkcję prostą, przyjmuje dwa parametry jako WYJŚCIE, Pierwszym z nich jest wskaźnik odniesienia do tablicy bajtów, a drugim jest Reference Int. Funkcja przydzieli ilość bajtów na podstawie niektórych reguł i zwróci wskaźnik pamięci oraz rozmiar bajtów i wartość zwracaną (1 dla sukcesu i 0 dla błędu).
Poniższy kod działa poprawnie i mogę dostać tablicę bajtów prawidłowo i liczbę bajtów i wartości zwracanej, ale gdy próbuję uwolnić pamięć za pomocą wskaźnika (IntPtr) otrzymuję wyjątek:
System Windows uruchomił punkt przerwania w pliku TestCppDllCall.exe.
Może to być spowodowane uszkodzeniem sterty, co oznacza błąd w pliku TestCppDllCall.exe lub dowolnej z bibliotek DLL, które zostały załadowane.
Przyczyną może być również naciśnięcie klawisza F12 przez naciśnięcie przycisku TestCppDllCall.exe.
Okno wyjściowe może zawierać więcej informacji diagnostycznych.
Żeby było jasne:
Następny C# kod działa poprawnie z innej funkcji DLL mają tę samą sygnaturę i uwalniając pamięć działa bez problemu.
Każda zmiana w kodzie (C) jest akceptowana, jeśli zachodzi potrzeba zmiany metody pamięci alokacji lub dodania dowolnego innego kodu.
Wszystkie funkcje potrzebne jest rodzimy funkcji DLL przyjąć dwa parametr odniesienia (tablicę bajtów oraz int w C# [IntPtr z tablicy bajtów i int]) wypełnić je z niektórych wartości opartych na regułach i zwrócić wynik funkcji (Sukces lub porażka).
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#:
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();
}
}
}
Kiedy debugowania kod ustawić przerwać punkt na linii (powrót 1) i wartość buforu:
myBuffer = 0x031b4fc0 "ABCDEFGHIJKLMNOPQRSTUVWXYZ««««««««î"
I otrzymałem tę samą wartość w kodzie C#, gdy zwrot wezwania do funkcji i wartość była:
52121536
Wynik Mam poprawny wskaźnik pamięci i jestem w stanie uzyskać wartość tablicy bajtów, jak zwolnić te bloki pamięci za pomocą tego wskaźnika w C#?
Proszę dać mi znać, jeśli coś nie jest jasne lub jeśli jest literówka, nie jestem native speakerem języka angielskiego.
Dziękuję za szybką odpowiedź. Mam nadzieję, że istnieje sposób na uwolnienie niezarządzanej pamięci w C# bezpośrednio. – khaled
Zgadzam się z dasblinkenlight: * najczystszym * sposobem jest prawdopodobnie dodanie "darmowej" metody do .dll. Ale Peter Ritchie też ma rację: jest oczywiście możliwe, że C# wywoła (poprzez interop) odpowiedni "wolny" (np. 'FreeCoTaskMem()') do jednak przydzielonego .dll (np. 'CoTaskMemAlloc()'). Jedną z rzeczy, których nie możesz * zrobić z C# jest "delete", jeśli .dll robi C++ "nowy". "malloc/free": OK. CoTaskMemAlloc/FreeCoTaskMem: również OK. "Nowy/bezpłatny": zdecydowanie * NIE *. – paulsm4
@ paulsm4 Proszę wyjaśnić, jak wywołać w C# malloc/free? – awattar