2012-06-11 13 views
3

假設這個C函數:要在P/Invoke中傳遞固定字符串的內容?

void do_something(const char* str) 

它存儲的字符串某處以後參考

此外,我在C#這個簽名來調用這個函數:

[DllImport("NativeLib")] 
static extern void do_something(string str); 

現在,我需要傳遞一個字符串,此方法時要做到:

  • 我需要釘住字符串(與GCHandle.Alloc())(或編組人員創建副本)?
  • 如果確實需要固定它,我是否會傳遞「原始」字符串(即我傳遞給GCHandle.Alloc()的字符串)?或者我需要通過返回值GCHandle.AddrOfPinnedObject()
  • string正確的數據類型(對於do_something)在這種情況下?或者我應該使用IntPtr而不是?
+0

當你說它「將字符串存儲在某個地方」時,你的意思是它會生成一個字符串的副本,還是說它只是存儲指針?這對你的問題的答案有很大的不同。如果您只是存儲指針,則需要使用GCHandle.Alloc()。編組人員另外進行TEMPORARY複製。 –

+0

它直接存儲點(不是副本)。 –

+0

您應該用'IntPtr'或'byte *'替換interop簽名中的字符串參數。 – CodesInChaos

回答

4

通過互操作邊界存儲指針是一個非常糟糕的想法,盡你所能修改C代碼來創建字符串的副本。

根據upvoted答案中的建議固定字符串不會起作用,pinvoke編組器必須將字符串轉換爲char *並在進行本地調用後釋放該轉換後的字符串,使指針無效。你必須自己整理絃樂。將參數聲明爲IntPtr並使用Marshal.StringToHGlobalAnsi()創建指針值。

或者使用Marshal.StringToCoTaskMemAnsi(),它從COM堆中分配。這是你真正的問題,沒有很好的方案來釋放字符串。 C代碼不能釋放字符串,因爲它不知道它是如何分配的。您的C#代碼不能釋放該字符串,因爲它不知道C代碼何時完成指針。這就是爲什麼複製字符串非常重要。如果您只進行幾次此呼叫,您可以忍受內存泄漏。

0

鑑於非託管代碼將存儲指針而不是複製字符串,我建議您使用GCHandle.Alloc()手動創建字符串的副本並將其作爲IntPtr傳遞。

然後,您將能夠管理傳遞的內存塊的生命週期,只有在完成後才能釋放它(通過GCHandle)。

+0

我不清楚你的建議。你想用'GCHandle.Alloc'固定字符串嗎?這是一個壞主意。 – CodesInChaos

+0

對不起,回答不完整。我正在考慮使用GCHandle.Alloc()獲取字符串的內存,然後使用Marshal.AllocHGlobal()分配一些全局內存並使用Marshal.Copy()將字符串複製到其中。然而,漢斯已經建議StringToHGlobalAnsi,這顯然更好。 –