C# 调用SysFreeString()时发生堆损坏错误
//------------------------------C代码------------------------------C# 调用SysFreeString()时发生堆损坏错误,c#,c++,marshalling,C#,C++,Marshalling,//------------------------------C代码------------------------------ [DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] extern static void PassStringOut([MarshalAs(UnmanagedType.BStr)] ou
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void PassStringOut([MarshalAs(UnmanagedType.BStr)] out String str);
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void FreeString([MarshalAs(UnmanagedType.BStr)] String str);
static void Main(string[] args)
{
String str;
PassStringOut(out str);
FreeString(str);
}
void PassStringOut(__out BSTR* str)
{
const std::string stdStr = "The quick brown fox jumps over the lazy dog";
_bstr_t bstrStr = stdStr.c_str();
*str = bstrStr.copy();
}
void FreeString(BSTR str)
{
SysFreeString(str);
}
//------------------------------------C+代码------------------------------
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void PassStringOut([MarshalAs(UnmanagedType.BStr)] out String str);
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void FreeString([MarshalAs(UnmanagedType.BStr)] String str);
static void Main(string[] args)
{
String str;
PassStringOut(out str);
FreeString(str);
}
void PassStringOut(__out BSTR* str)
{
const std::string stdStr = "The quick brown fox jumps over the lazy dog";
_bstr_t bstrStr = stdStr.c_str();
*str = bstrStr.copy();
}
void FreeString(BSTR str)
{
SysFreeString(str);
}
PassStringOut()和FreeString()中的'str'指针的值不同,在调用SysFreeString()时出现堆损坏错误。我应该通过引用FreeString()传递'str'吗?如果是这样,那么在C++和C++中应该使用什么语法?< p>封送处理层将在托管内存中分配字符串副本。该副本将由垃圾收集器释放。您不必在C#中使用
SysFreeString
aString
,事实上,正如您所发现的,尝试这样做是破坏堆的一种很好的方法
我是否应该认为将在字符串上执行两个副本?*str=bstrStr.copy()代码>然后按编组层
让我更详细地描述一下这里发生了什么
您的Main
方法调用非托管代码,传递String
类型的本地变量的托管地址。封送处理层创建自己的大小合适的存储来保存BSTR
,并将对该存储的引用传递给非托管代码
非托管代码分配一个string
对象,该对象引用与文本相关联的存储,然后分配一个BSTR
,并将原始字符串的第一个副本放入已分配的堆BSTR
。然后,它生成该BSTR
的第二个副本,并用对该存储器的引用填充out参数。bstrStr
对象超出范围,其析构函数释放原始的BSTR
然后,封送处理层生成适当大小的托管字符串,并第三次复制该字符串。然后释放传递给它的BSTR
。控件返回到您的C#代码,该代码现在有一个托管字符串
该字符串被传递到FreeString
。封送处理层分配一个BSTR
,并第四次将该字符串复制到BSTR
,并将其传递给非托管代码。然后它释放一个它不拥有的BSTR
,并返回。封送处理层释放它分配的BSTR
,从而损坏堆
托管堆保持未损坏状态;在垃圾收集器选择时,垃圾收集器将释放托管字符串
我应该通过引用FreeString()传递'str'吗
不可以。相反,您应该停止编写互操作代码,直到您对编组的各个方面都有了透彻和深入的了解
在托管代码和非托管代码之间编组数据是很困难的,即使是专家也很难做到正确。我的建议是,如果需要的话,您可以后退一大步,获得专家的服务,专家可以教您如何安全、正确地编写互操作代码。这与您认为的工作方式不同。pinvoke marshaller已经自动释放了BSTR。调用PassStringOut()时,封送拆收器将其转换为System.String并释放BSTR。这是在本机代码和托管代码之间传递BSTR的正常且必要的协议
FreeString()中的错误在于pinvoke marshaller分配了一个新的BSTR。它被发布了两次。首先是您的本地代码,再次是pinvoke marshaller。Kaboom来自调试堆,当您在附加调试程序的情况下运行代码时使用该调试堆
你帮了太多忙了,不要调用FreeString()
您可以让pinvoke marshaller为您处理ANSI字符串,这实际上是默认行为,因为它们在遗留C代码中非常常见。你的C++函数可以是这样的:
extern "C" __declspec(dllexport)
void __stdcall PassStringOut(char* buffer, size_t bufferLen)
{
const std::string stdStr = "The quick brown fox jumps over the lazy dog";
strcpy_s(buffer, bufferLen, stdStr.c_str());
}
使用匹配的C#代码:
但是,必须猜测缓冲区的正确大小才是最重要的细节。那么我是否应该认为将对字符串执行两个副本?“*str=bstrStr.copy();”然后通过编组层?@Stephenosella:将有四个原始字符串的副本;其中三个副本放入BSTR
s中,一个副本放入托管字符串中。最后一个副本被双重释放,从而损坏堆。更多细节请参见我的答案。好的。我理解。最后,我需要从STD::C++中的String到Cype中的String。我事先不知道所需std::string的大小。我之前已经发布过这篇文章。使用BSTR作为媒介似乎是最优雅的,尽管它需要构建一个_BSTR_t,但由于开销的原因,我并不特别喜欢它。对于我试图使用的,什么是最好的方法?BSTR没有错,pinvoke marshaller非常喜欢它。这也没什么问题,它至少可以帮助您将std::string转换为Unicode。如果不想使用它,则需要MultiByteToWideChar()转到Unicode,并需要SysAllocString()获取BSTR。同样的事情。由于转换,这里总是有开销,std::string只是上个世纪的字符串类型。如果您想删除BSTR中间人,然后传递一个初始化的StringBuilder,并立即传递给MultiByteToWideChar(),那么您的函数必须采用wchar\u t*。实际上,我所连接的后端代码(在C++中)严格是ANSI。因此,我将从后端代码(我模拟的)获得的std::字符串将是ANSI。因此,除了字符串在C#中的表示方式外,我不需要将字符串转换为Unicode。考虑到这一点,使用BSTR和BSTR\t仍然是最好的方法吗?我发布了一个替代方案。您的替代方案对我的用例不起作用,因为我事先不知道所需的缓冲区大小。如果我使用“UnmanagedType.AnsiBStr”作为“marshallas”参数,那么