C# Marshalling a C++ struct with wchar_t* member occasionally leaves the heap corrupted

六月ゝ 毕业季﹏ 提交于 2019-12-26 06:04:01

问题


I have declared a struct as follows:

// C++
struct TestStruct
{
    wchar_t* TestString;
};

and the corresponding managed representation

// C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TestStruct
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string TestString;
}

As well as this function:

// C++
__declspec(dllexport) void __stdcall FillMultipleStructs(TestStruct* testStructures, const short arrayLength)
{   
    for(int i = 0; i < arrayLength; i++)
    {
        const wchar_t stringToAllocate[] = L"foo";
        const unsigned long size = wcslen(stringToAllocate) * sizeof(wchar_t) + sizeof(wchar_t);
        wchar_t* allocatedString = static_cast<wchar_t*>(::CoTaskMemAlloc(size));
        wcscpy_s(allocatedString, size, stringToAllocate);

        (&testStructures[i])->testString = allocatedString;
    }
}

which is called by the FillMultipleStructs method, that takes multiple TestStructs and initializes them in the C++ code.

// C#
[DllImport("the path", CallingConvention = CallingConvention.StdCall, EntryPoint = "FillMultipleStructs", ExactSpelling = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
private static extern void _FillMultipleStructs([In, Out] TestStruct[] structures, [MarshalAs(UnmanagedType.I2)] short arrayLength);

public static IEnumerable<TestStruct> FillMultipleStructs(IEnumerable<TestStruct> structures)
{
    TestStruct[] structuresArray = structures.ToArray();
    _FillMultipleStructs(structuresArray, (short) structuresArray.Length);
    return structuresArray;
}

Calling the code works like this:

FillMultipleStructs(
    new List<TestStruct>()
    {
        new TestStruct(),
        new TestStruct(),
        new TestStruct()                       
    });

Now, the question is: sometimes, the code works, however, sometimes I get an a heap has been corrupted error. I do not understand where that comes from nor why it does work occasionally and sometimes it does not.

I guess it has to do with the marshalling of the struct's string member, so, if anyone can tell me where my error is or if anyone can point me in the right direction or show me the proper way to do that, I would gladly appreciate that.


回答1:


For anyone coming across this question struggling with the same problem, to summarize what pstrjds already said in a comment:

marshal as a BSTR and then instead of the CoTaskMemAlloc call, create BSTR objects

which effectively means changing the C# struct's definition from

[MarshalAs(UnmanagedType.LPWStr)]
public string TestString;

to

[MarshalAs(UnmanagedType.BStr)]
public string TestString;

and instead of using ::CoTaskMemAlloc to allocate a string in C++, ::SysAllocString needs to be used.

I did not have to change the signature of the C++ struct, since BSTR (in my case) was in the end a typedef for wchar_t*.



来源:https://stackoverflow.com/questions/46877031/c-sharp-marshalling-a-c-struct-with-wchar-t-member-occasionally-leaves-the-he

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!