Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/131.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 调用SysFreeString()时发生堆损坏错误_C#_C++_Marshalling - Fatal编程技术网

C# 调用SysFreeString()时发生堆损坏错误

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

//------------------------------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);
}
//------------------------------------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
a
String
,事实上,正如您所发现的,尝试这样做是破坏堆的一种很好的方法

我是否应该认为将在字符串上执行两个副本?
*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”参数,那么