从托管C#释放非托管内存的指针它(Release unmanaged memory from man

2019-06-27 19:57发布

The question in short words is : How to free memory returned from Native DLL as ItrPtr in managed code?

Details : Assume we have simple function takes two parameters as OUTPUT, The first one is Reference Pointer to byte array and the second one is Reference Int . The function will allocate amount of bytes based on some rules and return the pointer of memory and the size of bytes and the return value (1 for success and 0 for fail) .

The code below works fine and I can get the byte array correctly and the count of bytes and the return value, but when I try to free the memory using the pointer (IntPtr) I get exception :

Windows has triggered a breakpoint in TestCppDllCall.exe.

This may be due to a corruption of the heap, which indicates a bug in TestCppDllCall.exe or any of the DLLs it has loaded.

This may also be due to the user pressing F12 while TestCppDllCall.exe has focus.

The output window may have more diagnostic information.

To make things clear :

  1. The next C# code work correctly with other DLL function have the same signature and freeing the memory works without any problem .

  2. Any modification in (C) code accepted if you need to change allocation memory method or adding any other code .

  3. All the functionality I need is Native DLL function accept Two Parameter by reference (Byte array and int , In c# [IntPtr of byte array and int]) fill them with some values based on some rules and return the function result (Success or Fail) .


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# code :

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();
        }
    }
}

When I debug this code i set break point in the line (return 1) and the value of the buffer was :

myBuffer = 0x031b4fc0 "ABCDEFGHIJKLMNOPQRSTUVWXYZ‎‎‎‎««««««««î‏"

And I got the same value in C# code when the function call return and the value was :

52121536

The result I Got the correct Memory pointer and i am able to get the byte array value , how to free these memory blocks with this pointer in C# ?

Please let me know if there anything is not clear or if there any typo, I am not native English speaker .

Answer 1:

简短的回答:你应该在释放内存为您的DLL添加一个单独的方法。

龙答:有,其中存储可以在你的DLL实现内部分配不同的方式。 你释放内存的方式必须在您已分配的内存的方式匹配。 例如,存储器与分配new[]用方括号)需要与被释放delete[]相对于deletefree )。 C#不提供一种机制,为你做到这一点; 您需要将指针返回给C ++。

extern "C" CPPDLL_API void freeBuffer(unsigned char* myBuffer) {
    delete[] myBuffer;
}


Answer 2:

如果您分配在本地代码你自己的内存,使用CoTaskMemAlloc ,您可以在使用托管代码释放指针Marshal.FreeCoTaskMemCoTaskMemAlloc被描述为“在基于COM的应用共享存储器的唯一方法”(见http://msdn.microsoft.com/en-us/library/windows/desktop/aa366533(v=vs.85).aspx )

如果您需要使用与分配内存CoTaskMemAlloc与本地C ++对象,你可以使用放置新的一样,如果初始化内存new使用操作。 例如:

void * p = CoTaskMemAlloc(sizeof(MyType));
MyType * pMyType = new (p) MyType;

这与不分配内存new只是呼吁预分配内存的构造。

调用Marshal.FreeCoTaskMem不会调用类型的析构函数(这是没有必要,如果你只需要释放内存); 如果你需要通过调用析构函数做多的可用内存越多,你就必须提供这是否和P / Invoke的是一个本地方法。 通过本机类实例,以托管代码无论如何都不会支持。

如果你需要与其他一些API分配内存,您将需要通过P /调用,以释放其在托管代码中揭露它的托管代码。



Answer 3:

  HeapFree(GetProcessHeap(), 0, byteArrayPointer);

不,那是行不通的。 堆句柄是错误的,CRT与HeapCreate创建自己的堆()。 它埋在CRT数据,你不能得到它。 你可以在技术上找到手感从GetProcessHeaps(回),但你不知道它是哪一个。

指针可能是错误的也一样,CRT可能已添加由HeapAlloc()返回到存储调试数据指针一些额外的信息。

你需要导出调用delete []来释放缓冲的功能。 或者写一个C ++ / CLI包装,所以你可以使用在包装删除[]。 与C ++代码,并且所述包装使用CRT DLL的确切相同版本的额外需求(/必需MD)。 这几乎总是需要,你可以重新编译的C ++代码。



文章来源: Release unmanaged memory from managed C# with pointer of it