C# 封送具有 wchar_t* 成员的 C++ 结构有时会使堆损坏

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

本文关键字:结构 C++ 损坏 wchar 成员      更新时间:2023-10-16

我声明了一个struct,如下所示:

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

和相应的托管表示形式

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

以及此功能:

// 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;
    }
}

它由 FillMultipleStructs 方法调用,该方法采用多个TestStructs并在C++代码中初始化它们。

// 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;
}

调用代码的工作方式如下:

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

现在,问题是:有时,代码有效,但是,有时我会收到a heap has been corrupted错误。我不明白这是从哪里来的,也不明白为什么它偶尔会起作用,有时也不会。

我想这与struct弦成员的编组有关,所以,如果有人能告诉我我的错误在哪里,或者如果有人能指出我正确的方向或告诉我正确的方法,我将不胜感激。

对于遇到这个问题的人,请总结一下 pstrjds 在评论中已经说过的话:

元帅作为BSTR,然后代替CoTaskMemAlloc电话, 创建BSTR对象

这实际上意味着将C# struct的定义从

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

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

而不是使用 ::CoTaskMemAllocC++ 中分配字符串,需要使用 ::SysAllocString

我不必更改C++ struct的签名,因为BSTR(就我而言)最终是wchar_t*typedef