2012-05-13 38 views
1

我有一個很奇怪的問題:.NET interop中是否有內存安全級別?

我正在測試使用NUnit的非託管C DLL的幾個函數調用。奇怪的是,測試運行正常時會失敗,但是當我使用調試器運行它時(即使沒有中斷點),它也會正常傳輸。

因此,作爲普通的NUnit應用程序,調試器有更廣泛的內存訪問嗎?

我已隔離失敗的通話。它傳遞一個字符串指向一個字符串,編組人員應該將其轉換爲一個C#字符串。 C面看起來是這樣的:

#define get_symbol(a) ((a).a_w.w_symbol->s_name) 
EXTERN char *atom_get_symbol(t_atom *a); 

... 

char *atom_get_symbol(t_atom *a) { 
    return get_symbol(*a); 
} 

和C#代碼:

[DllImport("csharp.dll", EntryPoint="atom_get_symbol")] 
[return:MarshalAs(UnmanagedType.LPStr)] 
private static extern string atom_get_symbol(IntPtr a); 

這是從C返回的指針頗深列表的代碼和部分內。那麼我只是錯過了一些安全設置?

編輯:這裏是例外,我得到:

System.AccessViolationException:(翻譯成英文:)有讀取或寫入保護內存的嘗試。這可能表示其他內存已損壞。

at Microsoft.Win32.Win32Native.CoTaskMemFree(IntPtr ptr) 
at ....atom_get_symbol(IntPtr a) 

SOLUTION:

的問題是,該編組想釋放這是一個C結構的一部分的存儲器中。但它只是前人的精力使字符串的副本,並留下記憶的是:

[DllImport("csharp.dll", EntryPoint="atom_get_symbol")] 
private static extern IntPtr atom_get_symbol(IntPtr a); 

,然後在代碼中獲取字符串的副本:

var string = Marshal.PtrToStringAnsi(atom_get_symbol(ptrToStruct)); 

太棒了!

+0

「失敗」是什麼意思? –

+0

我收到一個異常,指出在C#調用atom_get_symbol時嘗試讀取或寫入受保護的內存。我檢查了作爲參數傳遞的IntPtr參數,它有一個有效的值。當我使用相同的IntPtr到列表從C代碼返回一個浮點數時,它工作正常。所以它只有一個問題,當它應該返回一個字符串... – thalm

+0

你如何分配你通過atom_get_symbol從託管代碼傳遞的IntPtr? –

回答

4

這將永遠導致在Vista和以上的崩潰,你怎麼避免它根本不是很清楚。堆棧跟蹤告訴故事,pinvoke編組器試圖釋放爲字符串分配的字符串緩衝區。它總是使用CoTaskMemFree()來做到這一點,這是對可能用於爲字符串分配內存的分配器的唯一合理猜測。但是,這很少奏效,C或C++代碼幾乎總是使用CRT的私有堆。這不會在XP上崩潰,它有一個更寬容的內存管理器。這會產生不可知的內存泄漏。

值得注意的是,C聲明並沒有給出太多的承諾,您可以調用該函數,它不會返回const char*。您唯一的希望是將返回類型聲明爲IntPtr而不是字符串,因此pinvoke編組器不會嘗試釋放指向內存。您需要使用Marshal.PtrToStringAnsi()將返回的IntPtr轉換爲字符串。

你需要測試一下它,調用函數10億次以確保你不會泄漏內存。如果該測試崩潰與OutOfMemoryException,那麼你有一個大問題。唯一的選擇是用C++/CLI語言編寫一個包裝器,並確保它使用與本機代碼完全相同的CRT版本,以便它們都使用相同的堆。如果你沒有源代碼,這是棘手和不可能的。這個函數很難從包括C在內的任何語言中調用。它應該被聲明爲int atom_get_symbol(t_atom* a, char* buf, size_t buflen),因此可以使用由客戶端代碼分配的緩衝區來調用它。

+0

該函數只是一個間接鏈,它可以分配哪些內存? – Dani

+0

C字符串需要內存來存儲字符。 C代碼管理內存本身在技術上是可行的,但它應該返回一個const char *來防止客戶端代碼與該字符串混淆。並且承擔客戶端代碼存儲指針並在C代碼釋放內存之後使用它的相當大的風險。唯一安全的方法是返回字符串的副本。在需要由調用者釋放的內存中。 –

+0

該字符串是一個結構的成員,因此破壞結構的代碼可能釋放該字符串。返回字符串的副本不是壞事,調用者可以複製字符串(假定文檔指定了字符串的生命週期)。 – Dani