C# 编组具有 wchar_t* 成员的 C++ 结构偶尔会使堆损坏
C# Marshalling a C++ struct with wchar_t* member occasionally leaves the heap corrupted
我声明了一个 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 已经在评论中说过的话:
marshal as a BSTR
and then instead of the CoTaskMemAlloc
call,
create BSTR
objects
这实际上意味着将 C#
struct
的定义从
更改为
[MarshalAs(UnmanagedType.LPWStr)]
public string TestString;
至
[MarshalAs(UnmanagedType.BStr)]
public string TestString;
而不是使用 ::CoTaskMemAlloc
在 C++
中分配字符串,需要使用 ::SysAllocString
。
我不必更改 C++
struct
的签名,因为 BSTR
(在我的例子中)最终是 typedef
for wchar_t*
.
我声明了一个 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 已经在评论中说过的话:
marshal as a
BSTR
and then instead of theCoTaskMemAlloc
call, createBSTR
objects
这实际上意味着将 C#
struct
的定义从
[MarshalAs(UnmanagedType.LPWStr)]
public string TestString;
至
[MarshalAs(UnmanagedType.BStr)]
public string TestString;
而不是使用 ::CoTaskMemAlloc
在 C++
中分配字符串,需要使用 ::SysAllocString
。
我不必更改 C++
struct
的签名,因为 BSTR
(在我的例子中)最终是 typedef
for wchar_t*
.