Câu hỏi trong các từ ngắn gọn là: Làm thế nào để giải phóng bộ nhớ được trả về từ Native DLL như ItrPtr trong mã được quản lý?Phát hành bộ nhớ không được quản lý từ quản lý C# bằng con trỏ của nó
Chi tiết: Giả sử chúng ta có hàm đơn giản nhận hai tham số là OUTPUT, Giá trị đầu tiên là Pointer tham chiếu đến mảng byte và tham số thứ hai là tham chiếu Int. Hàm sẽ phân bổ số byte dựa trên một số quy tắc và trả về con trỏ của bộ nhớ và kích thước của byte và giá trị trả về (1 cho thành công và 0 cho thất bại).
Đoạn code dưới đây hoạt động tốt và tôi có thể nhận được các mảng byte một cách chính xác và đếm byte và giá trị trả về, nhưng khi tôi cố gắng để giải phóng bộ nhớ bằng cách sử dụng con trỏ (IntPtr) tôi nhận được ngoại lệ:
Windows đã kích hoạt điểm ngắt trong TestCppDllCall.exe.
Điều này có thể do hỏng heap, cho biết lỗi trong tệp TestCppDllCall.exe hoặc bất kỳ tệp DLL nào mà nó đã tải.
Điều này cũng có thể do người dùng nhấn F12 trong khi TestCppDllCall.exe đã tập trung.
Cửa sổ đầu ra có thể có thêm thông tin chẩn đoán.
Để làm cho mọi việc rõ ràng:
Tiếp theo C# code làm việc một cách chính xác với chức năng DLL khác có chữ ký giống nhau và giải phóng bộ nhớ hoạt động mà không có vấn đề gì.
Bất kỳ sửa đổi nào trong mã (C) được chấp nhận nếu bạn cần thay đổi phương thức bộ nhớ phân bổ hoặc thêm bất kỳ mã nào khác.
Tất cả chức năng tôi cần là hàm DLL gốc chấp nhận hai tham số bằng tham chiếu (mảng byte và int, trong C# [IntPtr của mảng byte và int]) điền chúng với một số giá trị dựa trên một số quy tắc và trả về kết quả hàm (Thành công hay Thất bại).
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# mã:
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();
}
}
}
Khi tôi gỡ lỗi mã này tôi đặt chia điểm trong dòng (return 1) và giá trị của bộ đệm là:
myBuffer = 0x031b4fc0 "ABCDEFGHIJKLMNOPQRSTUVWXYZ««««««««î"
Và tôi có cùng giá trị trong mã C# khi hàm trả về cuộc gọi hàm và giá trị là:
52121536
Kết quả Tôi có con trỏ Bộ nhớ chính xác và tôi có thể nhận giá trị mảng byte, cách giải phóng các khối bộ nhớ này bằng con trỏ này trong C#?
Vui lòng cho tôi biết nếu có bất kỳ điều gì không rõ ràng hoặc nếu có lỗi đánh máy, tôi không phải là người nói tiếng Anh bản ngữ.
Cảm ơn bạn đã phản hồi nhanh. Tôi hy vọng rằng có một cách để giải phóng bộ nhớ không được quản lý trong C# trực tiếp. – khaled
Tôi đồng ý với dasblinkenlight: cách * cleanest * được cho là thêm phương thức "miễn phí" vào .dll của bạn. Nhưng Peter Ritchie cũng đúng: chắc chắn C# có thể gọi (qua interop) một "miễn phí" tương ứng (ví dụ: 'FreeCoTaskMem()') tuy nhiên phân bổ .dll (ví dụ: CoTaskMemAlloc() '). Một điều bạn * không thể * làm từ C# là "xóa", nếu dll thực hiện một C++ "mới". "malloc/free": OK. CoTaskMemAlloc/FreeCoTaskMem: cũng OK. "Mới/miễn phí": chắc chắn * KHÔNG *. – paulsm4
@ paulsm4 Vui lòng giải thích cách gọi trong C# malloc/miễn phí? – awattar