2

I have this function in a dll in C and I cannot change it:

extern "C" SIBIO_MULTILANGUAGE_API_C DWORD getLabel(const char* const i_formName, 
                                                    const char* const i_fieldName, 
                                                    wchar_t** i_output);

I know that this call inside allocates the memory for the wchar_t* using the function CoTaskMemAlloc.

In C# I wrapped this function in this way:

[DllImport("sibio_multilanguage_c.dll", EntryPoint = "getLabel", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 _getLabel([In] string i_formName, [In] string i_fieldName, 
                                       [MarshalAs(UnmanagedType.LPWStr)] out string i_output);

static public string getLabel(string i_formName, string i_fieldName)
{
    string str = null;
    UInt32 err = _getLabel(i_formName, i_fieldName, out str);
    if (0 != err)
    {
        throw  new System.IO.FileNotFoundException();
    }
    return str;
}

I'm able to read correctly the content of the wchar_t* but reading in this way I don't free the memory allocated in the C function.

How can I read the wchar_t* and also be able to free it? Any help is greatly appreciated!

BugsFree
  • 540
  • 1
  • 6
  • 24
  • 1
    I believe you need to call `Marshal.FreeCoTaskMem`, however it expects an `IntPtr` parameter instead of a `String` instance. You could change your `getLabel` import to use `IntPtr` and do the string conversion yourself. – Dai Oct 10 '16 at 11:14
  • You are right. I thought about it and I also tried it, but in that way I wasn't able to read the content correctly. Probably I did something wrong. Now I edit the question also with that attempt. – BugsFree Oct 10 '16 at 11:17
  • Have you checked whether that library provides an utility function to clean the memory of objects created within their other functions? – Anzurio Oct 10 '16 at 11:55
  • 1
    If you follow @Dai's suggestion, you should be able to convert the `IntPtr` to a `string` using [`Marshal.PtrToStringAuto` Method (`IntPtr`)](https://msdn.microsoft.com/en-us/library/ewyktcaa.aspx). (Thanks to [this answer](http://stackoverflow.com/a/9175905/5264491).) – Ian Abbott Oct 10 '16 at 12:00
  • 1
    This is not necessary, the pinvoke marshaller already calls CoTaskMemFree(). Just feel better about it by writing a little unit test that calls the function a billion times. – Hans Passant Oct 10 '16 at 14:52
  • I did a little unit test calling the function a billion times and commenting the line `Marshal.FreeCoTaskMem(i_result)`(see answer below), the memory grows at each iteration. – BugsFree Oct 10 '16 at 15:24

1 Answers1

0

Thanks to @Dai and @IanAbbot comments I've come up to a solution that works perfectly:

 [DllImport("sibio_multilanguage_c.dll", EntryPoint = "getLabel", CallingConvention = CallingConvention.Cdecl)]
 private static extern UInt32 _getLabel([In] string i_formName, [In] string i_fieldName, 
                                        out IntPtr i_output);

static public string getLabel(string i_formName, string i_fieldName)
{
    IntPtr i_result;
    string str = null;
    UInt32 err = _getLabel(i_formName, i_fieldName, out i_result);
    if (0 != err)
    {
        throw  new System.IO.FileNotFoundException();
    }
    str = Marshal.PtrToStringAuto(i_result);
    Marshal.FreeCoTaskMem(i_result);
    return str;
}
BugsFree
  • 540
  • 1
  • 6
  • 24