2015-02-05 17 views
1

我使用以下代碼從C#調用winapi函數。從C#調用Windows API函數時需要明確釋放哪些對象?

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] 
    public static extern IntPtr PathFindFileName(string p); 

    IntPtr pStr = PathFindFileName("Test"); 
    string str = Marshal.PtrToStringAuto(pStr) 

我想知道如何區分一旦完成需要釋放的對象和不需要的對象。

例如:在上面的代碼中,我是否需要釋放pStr?

還是垃圾收集器自動執行它?

萬一我需要釋放,它是如何完成的?

謝謝, 邁克爾。

+0

這段代碼很糟糕:'pStr'是一個懸掛指針。 – 2015-02-05 15:20:49

+0

@CoryNelson這就是這個人所問的。 – andlabs 2015-02-05 16:42:06

+1

@MichaelEngstler - 對於將來的筆記,您使用的問題標題不太好;更好的一個是「我需要從C#調用Windows API函數時明確釋放哪些對象?」或沿着這條線的東西; [閱讀本文](http://stackoverflow.com/help/how-to-ask),在「編寫一個總結特定問題的標題」下。 (這個問題本身*很好,至少它似乎只是讓我醒來) – andlabs 2015-02-05 16:45:29

回答

4

只有這樣,才能知道如何處理內存互操作是閱讀的文檔。

在這種情況下,文檔有點弱,並沒有明確說明發生了什麼。發生的是該函數返回的指針指向您提供的緩衝區中的某個點。這意味着您傳遞給函數的緩衝區必須在函數返回後纔有效,以便能夠從中讀取。而且,如您使用參數string的自動編組寫入p/invoke,傳遞給非託管函數的緩衝區將不受您的控制。你的代碼可能在某些時候似乎工作,但它肯定是壞的,並且可能會因爲相當糟糕的運行時錯誤而失敗。

您需要手動封送字符串,以便您可以控制您提供的緩衝區的生存期。

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] 
public static extern IntPtr PathFindFileName(IntPtr pPath); 

.... 

string path = ...; 
IntPtr pPath = Marshal.StringToHGlobalUni(path); 
try 
{ 
    IntPtr pFileName = PathFindFileName(pPath); 
    string fileName = Marshal.PtrToStringUni(pFileName); 
} 
finally 
{ 
    Marshal.FreeHGlobal(pPath); 
} 

這就是你問的問題的答案。但是,解決問題的最佳方法是避免互操作,並使用內置的.net庫功能進行路徑操作。

+0

值得一提的是,你需要這樣做的原因與原始問題的方式相比,原始文件中由pStr指向的內存可能會在Marshal.PtrToStringAuto返回正確的字符串之前被破壞。看起來它現在可以正常工作,但由於文件名已損壞,未來很可能會出現一些奇怪的錯誤。 – 2015-02-05 15:55:58

+0

@ Dave76我想我已經在第二段中提到過 – 2015-02-05 15:59:59

+0

你描述了這個問題,但沒有具體說明副作用。我認爲OP可能不理解使用原始代碼的分歧,因爲它可能在開發過程中正常工作。我同意你使用.NET Path類是更好的方法。 – 2015-02-05 16:33:12

2

這個特殊的WinAPI函數是一個棘手的函數。返回的指針指向輸入緩衝區。這意味着它不被分配,並且不應該由您發佈。此外,如果代碼正常工作,您就很幸運,因爲您需要輸入緩衝區爲活動狀態,直到PtrToStringAuto將其內容複製到受管字符串。

你真的需要調用這個特定的WinAPI函數嗎?您可以在.NET Framework庫的System.IO.Path類中找到相同的功能。

https://msdn.microsoft.com/en-us/library/system.io.path.aspx

你可能想Path.GetFileName

https://msdn.microsoft.com/en-us/library/system.io.path.getfilename.aspx

+0

感謝您的評論,我注意到了Path.GetFileName,但它與PathFindFileName稍有不同,所以它對我來說不夠好。你會如何建議調用這個API?如果我(以某種方式)分配原始字符串並將其自行釋放,會更好嗎? (爲了避免競爭狀況) – Michael 2015-02-05 15:13:23

+0

你最初問是否釋放內存。答案很簡單:不要釋放它。當你問如何使用這個函數時,我仍然認爲你應該結合Path類的更多功能來模仿你想要的行爲,而不是調用WinAPI。 – 2015-02-05 15:16:58

+0

@DavidHeffernan:你說的是錯誤的。該函數返回的指針指向其他地方分配的內存塊的中間。你永遠不要使用這個指針來釋放內存塊。因爲它不是一個內存塊。只有用作輸入參數的內存塊必須在這裏釋放,並且這是通過p/invoke自動完成的。這裏的問題是系統可能在用戶調用PtrToStringAuto之前釋放內存。 – 2015-02-05 16:47:07

相關問題