2016-03-02 39 views
4

我是從C#調用以下VC++方法C#互操作釋放存儲器在非託管代碼

__declspec(dllexport) unsigned char* Get_Version_String() 

如下分配:

internal static class NativeMethods 
{ 
    [DllImport("my.dll"), 
     CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, 
     CallingConvention = CallingConvention.Cdecl)] 
    internal static extern string Get_Version_String(); 
} 

上面的代碼是在其中面向.NET庫3.5。當我從3.5程序集中調用它時,它工作正常;從4.5組裝時調用它,但是,它會導致

0xC0000374:堆已損壞

閱讀this question後,我改變了我的方法調用如下:

[DllImport("my.dll", 
    EntryPoint = "Get_Version_String", 
    CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, 
    CallingConvention = CallingConvention.Cdecl)] 
private static extern IntPtr Get_Version_String_PInvoke(); 

internal static string Get_Version_String() 
{ 
    IntPtr ptr = Get_Version_String_PInvoke(); 
    string versionString = Marshal.PtrToStringAnsi(ptr); 
    return versionString; 
} 

這個按預期工作,但Hans Passant的答案帶有警告:

您找到的解決方法是正確的,編組不會嘗試釋放內存IntPtr。請注意,如果C代碼返回一個不需要釋放的const char*,這實際上只會實現良好的結果。如果情況並非如此,你會有永久的內存泄漏。

由於C++方法不返回const,我假設我的特定函數的解決方法將導致內存泄漏。

我無法更改原始方法,所以我找到了this other question,它討論瞭如何從管理代碼中釋放內存。但是,調用要麼Marshal.FreeHGlobal(ptr)Marshal.FreeCoTaskMem(ptr)也扔0xC0000374: A heap has been corrupted.

誰能
一)證實這種方法確實會從內存泄漏受苦,
B)如果是這樣,建議如何從指針釋放內存在託管代碼?

的C++方法主體,簡化的,具體如下:預先

unsigned char versionString[50]; 

__declspec(dllexport) unsigned char* Get_Version_String() 
{ 
    strcpy((char *) versionString, "Key1:[xx],Key2:[xx],Key3:[xx],Key4:[xx]"); 
    // string manipulation 
    return versionString; 
} 

謝謝,對不起,如果這是微不足道;我既不是C++也不是Interop專家。

+0

我可能會盡可能激發內存泄漏。如果這個非託管代碼可以被多次執行而沒有不必要的影響,那麼可以偶爾使用'GC.Collect()'調用將其打包到無限循環中,並觀察內存消耗。 – Edin

+0

好的,我可以測試一下。我應該如何「偶爾」調用GC.Collect()?每一分鐘?更長? – user5877732

+0

GC.Collect()不一定需要。如果您正在運行32位應用程序,則內存將大約爲2Gb。但是,這是一種很好的方式來查看內存是否被更早釋放,而不僅僅是等待OutOfMemoryException。多久調用一次取決於記憶分配的速度。如果這是緩慢的,那麼每分鐘就足夠了。但是,如果速度非常快,您可以每隔一秒左右調用一次。另請注意,每次調用GC.Collect()都不一定會導致垃圾回收。並且不要在生產性代碼中調用GC.Collect() - 在大多數情況下不需要。 – Edin

回答

5

任何人都可以證實這種方法確實會從記憶遭受泄漏

只有你能做到這一點,缺少常量關鍵字是不能保證本機代碼實際上不返回一個文字。 C代碼中的普遍錯誤btw。寫一個測試程序,調用函數一億次。如果你沒有看到使用任務管理器的內存使用爆炸,那麼你沒有問題。

如果是這樣,建議如何從託管代碼中的指針釋放內存?

你只是不能,它必須是調用free()的本機代碼本身。以便它使用正確的堆,即由該代碼使用的C運行時庫創建的堆。底層的winapi調用是HeapCreate(),你沒有堆句柄。從技術上講,GetProcessHeaps()是可以發現的,但你不知道哪一個是「正確」的。從VS2012開始,CRT使用GetProcessHeap()而不是HeapCreate(),現在Marshal.FreeHGlobal()可以工作。但是你知道這段代碼沒有,你必須要求作者或供應商進行更新。只要你這樣做,問他一個更有用的味道這個函數,它應該採取char *作爲參數。


一個更具建設性的方法是隻採取跨步內存泄漏。只需調用函數一次,在程序運行時版本號不會改變。因此將其存儲在靜態變量中。丟失大約80字節的地址空間並不是你可能注意到的問題,當你的程序終止時,操作系統會自動清理。

+0

感謝您將它納入視角並提供實用的解決方案。 – user5877732