2013-06-18 176 views
3

我在C#中使用非託管C++靜態庫的項目。當C#從非託管代碼中取回字符串時,我遇到了一個奇怪的錯誤。該程序崩潰,VS告訴我可能的堆損壞。該字符串是非託管代碼中對象的字段 - 因此它不是局部變量問題。奇怪的是,崩潰總是在調試模式下發生,但只有在實際運行程序時的某些特定情況下才會發生。此外,儘管調試崩潰在所有計算機上都是可重現的,但運行時崩潰只發生在某些計算機上。非託管C++代碼將字符串發送到託管代碼的問題

我要指出,我有一個從非託管代碼出口許多功能,其中沒有引起除了這個功能的問題,另一個是做幾乎同樣的事情(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#中的字符串來解決問題。

+0

'fileInfo'是'HashFile'中的一員?另一個線程是否可以修改'fileInfo'? – Henrik

+0

命名風格:我會使用**「native」**而不是_「非託管」_(_「native C++」_,_「managed C#」_)。 –

+0

@ Mr.C64謝謝你的筆記。 –

回答

2

問題就出在你的P /的CppToCsharpAdapter.GetFileInfo調用定義,你沒有跟你的問題包括。至少,它需要下列屬性補充說:

[return: MarshalAs(UnmanagedType.LPStr)] 

如果省略此屬性,則P /調用層將承擔返回的字符串代碼由BSTR表示,但你實際上是使用空值終止的ANSI字符串。此鏈接提供了更多信息:

Default Marshaling for Strings: Strings Used in Platform Invoke

+0

這可能是問題。謝謝。 –

0

你的DLL導出的GetFileInfo()函數返回一個原料const char*,但我不知道這是一個正確類型的託管代碼(除非您使用合適的P/Invoke簽名......)。

您是否考慮過退回BSTR而不是? BSTR是一個典型的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&,但我建議你只返回一個string按值(即只返回stringstring&)。

+0

按值返回是一個問題,因爲在本機託管代碼和託管代碼之間傳遞的變量在中間被擦除,並且最終在託管端上出現垃圾 –

+0

無法嘗試您的解決方案,因爲我的帶有導出功能的文件已定義與extern「C」不允許使用COM –

+0

@YechielLabunskiy:實際上,COM也可以很好地用於C語言。 –