2

So I created a custom dll callsed FileGuidUtils.dll written in C/C++ and one of the functions returns a WCHAR * string (as a LPWStr in C#). This string gets allocated heap memory inside the function of type (WCHAR *).

Right now I just use the returned string and that's it. Should I be freeing it somewhere in the C#? What code should I use if so? Or is the CLR garbage collector taking care of it for me?

    [DllImport(@"FileGuidUtils.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.LPWStr)]
    private static extern string getReparseTarget([MarshalAsAttribute(UnmanagedType.LPWStr)] string linkPath);

I occasionally get unhandled out of memory exceptions but am unable to pinpoint the cause because I don't get it very often. For right now I just wanna know if I'm handling the returned string properly?

Wes
  • 1,183
  • 3
  • 23
  • 51
  • See http://stackoverflow.com/q/5298268/613130 . What you are doing is wrong and will cause memory leaks – xanatos Apr 03 '15 at 09:21
  • What I'm doing is not totally analogous to him, I am using malloc for allocation and, unlike him, am able to use the returned string. I am just worried there is a leak occurring. Regardless I will give it a thorough read but if you could be more specific, that would be greatly appreciated. – Wes Apr 03 '15 at 09:26
  • See even http://stackoverflow.com/questions/7322503/pinvoke-how-to-free-a-mallocd-string – xanatos Apr 03 '15 at 09:30

1 Answers1

2

You are creating a memory leak. .NET can't free memory allocated by malloc, C++ new, or any other allocator of Windows, because it can't know which allocator was used (there are some exceptions on this).

Possible solutions:

1) Return a IntPtr:

private static extern IntPtr getReparseTarget([MarshalAsAttribute(UnmanagedType.LPWStr)] string linkPath);

and have a corresponding

private static extern void freeMemory(IntPtr ptr);

and then manually rebuild the C# string with PtrToStringUni()

2) Have a function that returns the length needed and then pass a StringBuilder of that length (new StringBuilder(len)):

private static extern int getReparseTargetLength([MarshalAsAttribute(UnmanagedType.LPWStr)] string linkPath);

private static extern void getReparseTarget([MarshalAsAttribute(UnmanagedType.LPWStr)] string linkPath, [MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder output, int maxLen);

3) Use MarshalAs(UnmanagedType.BSTR)

[return: MarshalAs(UnmanagedType.BSTR)]
private static extern string getReparseTarget([MarshalAsAttribute(UnmanagedType.LPWStr)] string linkPath);

The CLR automatically frees strings allocated as BSTR. Note that you will need to create them as BSTR C++-side (with SysAllocString/SysAllocStringLen).

xanatos
  • 109,618
  • 12
  • 197
  • 280