Search code examples
c#pinvokemarshallingnativemanaged

can native C **char be safely passed to managed C# delegate with paramter type "out string"?


In C:

extern "C" __declspec(dllexport) int CfgGetVariableString(const char *Name, char **Value)
{
    char StrValue[STR_MAX];
    int RetValue = GetVariableToStrValue(Name, StrValue, STR_MAX);
    if (RetValue == 0)
        *Value = StrValue;

    return RetValue;
}

C#

[DllImport(DllName, CallingConvention = DllCallingConvention)]        
private static extern int CfgGetVariableString([MarshalAs(UnmanagedType.LPStr)]string name, [MarshalAs(UnmanagedType.LPStr)]out string value);

This does not work. I can make it work by calling CoTaskMemAlloc, but then I guess I should free it with separate managed to native call?

So what is the cleanest way to do it?


Solution

  • I can make it work by calling CoTaskMemAlloc.

    That is the only way that it can work. Because the marshaller will call CoTaskMemFree to deallocate the memory returned from the native function.

    I guess I should free it with separate managed to native call?

    As mentioned above, that's not necessary because the framework already does so.

    What is the cleanest way to do it?

    In my opinion the cleanest approach here, since you are already using a string length determined at compile time, is to have the caller allocate the memory. Change the type from char** to char*. Add an extra argument containing the length of the allocated string. Use StringBuilder on the C# side. There are countless examples of this pattern available here and indeed in other places so I don't feel any need to add to the canon.