avicohh
avicohh

Reputation: 197

Free IntPtr allocated memory after promoting the pointer

I'm using IntPtr in C#, to get array of object from C++ project.

To do that, i allocated the memory needed with Marshal.AllocHGlobal method, and i'm copying the full array in C++ into the ptr.

The thing is I am promoting the pointer, to move to the next object in the array.

My question is: Do i have to restore the first value of the IntPtr, before using the Marshal.FreeHGlobal method?

Here is the code

public List<DirEntry> getDirEntries()
    {
            int dirEntrySize = Marshal.SizeOf(typeof(DirEntry));
            int bufferSize = 28 * dirEntrySize;
            IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
            CppToCsharpAdapter.getDirEntries(this.myDiskPointer, buffer);

            DirEntry dirEntry = new DirEntry();
            List<DirEntry> dirEntries = new List<DirEntry>();
            for (int i = 0; i < 28; i++)
            {
                Marshal.PtrToStructure(buffer, dirEntry);
                buffer += dirEntrySize;
            }

            Marshal.FreeHGlobal(buffer);
            return dirEntries;
    }

Upvotes: 1

Views: 2140

Answers (1)

AcidJunkie
AcidJunkie

Reputation: 1918

You have to create a copy of the original pointer which can be used later use. In this case, it is called current:

    public List<DirEntry> getDirEntries()
    {
        int dirEntrySize = Marshal.SizeOf(typeof(DirEntry));
        int bufferSize = 28 * dirEntrySize;
        IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
        CppToCsharpAdapter.getDirEntries(this.myDiskPointer, buffer);

        IntPtr current = buffer;

        DirEntry dirEntry = new DirEntry();
        List<DirEntry> dirEntries = new List<DirEntry>();
        for (int i = 0; i < 28; i++)
        {
            Marshal.PtrToStructure(current, dirEntry);
            current += dirEntrySize;
        }

        Marshal.FreeHGlobal(buffer);
        return dirEntries;
    }

However, there is another possibility without using an explicit copy of your pointer by using IntPtr.Add():

    public List<DirEntry> getDirEntries()
    {
        int dirEntrySize = Marshal.SizeOf(typeof(DirEntry));
        int bufferSize = 28 * dirEntrySize;
        IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
        CppToCsharpAdapter.getDirEntries(this.myDiskPointer, buffer);

        DirEntry dirEntry = new DirEntry();
        List<DirEntry> dirEntries = new List<DirEntry>();
        for (int i = 0; i < 28; i++)
        {
            Marshal.PtrToStructure(IntPtr.Add(buffer, i * dirEntrySize), dirEntry);
        }

        Marshal.FreeHGlobal(buffer);
        return dirEntries;
    }

Please keep in mind, that your code contains a potential memory leek. You should wrap your code in a try/finally block. In the finally block you free the memory block again if the pointer is not zero:

    public List<DirEntry> getDirEntries()
    {
        int dirEntrySize = Marshal.SizeOf(typeof(DirEntry));
        int bufferSize = 28 * dirEntrySize;
        IntPtr buffer = Marshal.AllocHGlobal(bufferSize);

        try
        {
            CppToCsharpAdapter.getDirEntries(this.myDiskPointer, buffer);

            DirEntry dirEntry = new DirEntry();
            List<DirEntry> dirEntries = new List<DirEntry>();
            for (int i = 0; i < 28; i++)
            {
                Marshal.PtrToStructure(IntPtr.Add(buffer, i * dirEntrySize), dirEntry);
            }
        }
        finally
        {
            if (buffer != IntPtr.Zero)
                Marshal.FreeHGlobal(buffer);
        }


        return dirEntries;
    }

Upvotes: 2

Related Questions