非托管 C++ 代码向托管代码发送字符串的错误

Posted

技术标签:

【中文标题】非托管 C++ 代码向托管代码发送字符串的错误【英文标题】:Bug with Unmanaged C++ code sending a string to Managed code 【发布时间】:2013-06-18 09:31:26 【问题描述】:

我有一个使用非托管 C++ 静态库的 C# 项目。当 C# 从非托管代码中取回字符串时,我遇到了一个奇怪的错误。程序崩溃,VS 告诉我可能的堆损坏。该字符串是非托管代码中对象中的一个字段 - 所以它不是局部变量问题。奇怪的是,崩溃总是在调试模式下发生,但仅在实际运行程序时的某些非常特殊的情况下才会发生。此外,虽然调试崩溃可以在所有计算机上重现,但运行时崩溃仅发生在某些计算机上。

我应该注意,我有许多从非托管代码导出的函数,除了这个函数和另一个做几乎相同事情的函数 (GetBlockInfo) 之外,它们都不会引起问题。

这是导致崩溃的代码行:

string info = CppToCsharpAdapter.GetFileInfo(myHashFilePointer);

CppToCsharpAdapter 是我的托管/非托管代码适配器。 CppToCsharpAdapter.GetFileInfo 调用在我的非托管代码中执行对 GetFileInfo 函数的调用。

这是.cpp中的导出功能:

__declspec(dllexport)   const   char* __stdcall  GetFileInfo(HashFile* THIS)

      return THIS->GetFileInfo().c_str();   

这是非托管代码中的 GetFileInfo 函数:

string& GetFileInfo()

    try
    
        LogicalHeaderBlock *infoBlock = LogicalFHBuffer;
        stringstream infoString;
        infoString<<infoBlock->getHashFuncID()<<endl;

             // many more lines//

        fileInfo = infoString.str();
        return fileInfo;

    
    catch(exception &e)
     throw e; 

这是导致崩溃的调用堆栈:

ntdll.dll!770a04e4()    
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] 
ntdll.dll!77062a2a()    
ntdll.dll!770a1b71()    
oleaut32.dll!75c43e59()     
[Managed to Native Transition]  
mscorlib.dll!System.StubHelpers.AnsiBSTRMarshaler.ClearNative(System.IntPtr pNative) + 0x45 bytes   
FMS_adapter.dll!FMS_adapter.HashFile.GetFileInfo() Line 249 + 0xb bytes C#

编辑:更新了调用堆栈,现在我得到了 [Managed to Native Transition] 行,这显然表明问题存在。

任何帮助将不胜感激。提前致谢。

编辑:我最终通过让 C# CppToCsharpAdapter.GetFileInfo() 函数返回一个 IntPtr,然后将其转换为 C# 中的字符串来解决问题。

【问题讨论】:

fileInfoHashFile 的成员吗?是否有其他线程正在修改fileInfo 命名风格:我会使用 "native" 而不是 "unmanaged" ("native C++", “托管 C#”). @Mr.C64 感谢您的来信。 【参考方案1】:

问题在于您对CppToCsharpAdapter.GetFileInfo 的 P/Invoke 定义,您的问题中没有包含该定义。至少需要添加以下属性:

[return: MarshalAs(UnmanagedType.LPStr)]

如果您省略此属性,P/Invoke 层将假定返回的字符串在代码中由 BSTR 表示,但您实际上使用的是以 null 结尾的 ANSI 字符串。此链接有更多信息:

Default Marshaling for Strings: Strings Used in Platform Invoke

【讨论】:

这可能是问题所在。谢谢。【参考方案2】:

您的 DLL 导出的 GetFileInfo() 函数返回 raw const char*,但我不确定这是托管代码的正确类型(除非您使用正确的 P/Invoke 签名。 ..)。

您是否考虑过返回 BSTRBSTR 是一个典型的COM 字符串,我认为.NET 对COM 的理解非常好,所以它也可以自动释放本机代码返回的COM 分配的字符串。

__declspec(dllexport) BSTR __stdcall GetFileInfo(....)

    ....

    // Assume that "nativeString" is "const char*".

    // We first convert from ANSI/MBCS to Unicode:
    // (CA2W is a conversion helper defined in <atlconv.h>)
    CA2W unicodeNativeString( nativeString );

    // ...and then we return a BSTR built from it:
    return ::SysAllocString( unicodeNativeString );


编辑:

作为旁注,另一个(不是 DLL 导出的)GetFileInfo() 函数返回一个 string&amp;,但我建议您只返回一个 string 按值 (即只返回string string&amp;)。

【讨论】:

按值返回是一个问题,因为在本机代码和托管代码之间传递的变量在中间被擦除了,我最终在托管端得到了垃圾 无法尝试您的解决方案,因为我的带有导出功能的文件是用不允许使用 COM 的 extern "C" 定义的 @YechielLabunskiy:实际上,COM 也可以很好地从 C 中使用。 哦,我不知道怎么做。当我尝试包含 comutil 标头来尝试您的解决方案时,我的导出功能给出了各种错误。感谢您的帮助。

以上是关于非托管 C++ 代码向托管代码发送字符串的错误的主要内容,如果未能解决你的问题,请参考以下文章

让非托管 c++ 代码调用调用 c# 代码的托管 c++ 代码

托管和非托管代码错误 C3699

C# 托管非托管代码

C# 调用非托管 C++ 返回平方符号字符串

如何在 C++ 非托管代码 Json 中反序列化一个字节 [] 的 json 字符串?

尝试从 C++/CLI 调用非托管 C++ 时解决错误