Passing an array of strings from C# to C++ DLL fun

2019-08-17 05:44发布

问题:

I am writing a C# application that passes an empty string array of size 30 to a C++ DLL. This string array needs to be filled in the DLL and given back to the C# application.

I my code I observe memory corruption at the end of function call from DLL.

My C++ DLL code is as follows:

SAMPLEDLL_API BOOL InitExecution (wchar_t **paszStrings, int count)
{
    for (int i = 0 ; i < count; i++)
    {
        mbstowcs(*(paszStrings + i), "Good",4);
        //*(paszStrings + i) = "Good";
    }
return TRUE;
}

My C# code is

string[] names = new[] { "Britto", "Regis" };
if (Wrapper1.InitExecution(ref names, names.Length) == 1)
    MessageBox.Show("Passed");

[DllImport("MFCLibrary1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 InitExecution(ref string[] Names, int count);

回答1:

To make this current approach work you'd need to pass StringBuilder instances rather than string. That's because the data is flowing from caller to callee. The strings are out parameters. And that means that the caller has to allocate the buffers for each string, and know how large the buffers need to be.

It's much easier to use BSTR here. This allows you to allocate the strings in the native code, and have them deallocated in the managed code. That's because BSTR is allocated on the shared COM heap, and the p/invoke marshaller understands them. Making this slight change means that the caller does not need to know how large the strings are up front.

The code would look like this:

SAMPLEDLL_API BOOL InitExecution(BSTR* names, int count)
{
    for (int i = 0 ; i < count; i++)
        names[i] = SysAllocStr(...);
    return TRUE;
}

And on the C# side you write it like this:

[DllImport(@"mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitExecution(
    [Out] IntPtr[] names, 
    int count
);

And then you've got a bit of work to marshal from BSTR to C# string.

IntPtr[] namePtrs = new IntPtr[count];
InitExecution(namePtrs, namePtrs.Length);
string[] names = new string[namePtrs.Length];
for (int i = 0; i < namePtrs.Length; i++)
{
    names[i] = Marshal.PtrToStringBSTR(namePtrs[i]);
    Marshal.FreeBSTR(namePtrs[i]);
}