2009-12-11 39 views
22

我有以下結構:Marshal.AllocHGlobal VS Marshal.AllocCoTaskMem,Marshal.SizeOf VS的sizeof()

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 
public struct WAVEHDR 
{ 
    internal IntPtr lpData; // pointer to locked data buffer 
    internal uint dwBufferLength; // length of data buffer 
    internal uint dwBytesRecorded; // used for input only 
    internal IntPtr dwUser; // for client's use 
    internal uint dwFlags; // assorted flags (see defines) 
    internal uint dwLoops; // loop control counter 
    internal IntPtr lpNext; // reserved for driver 
    internal IntPtr reserved; // reserved for driver 
} 

我需要分配非託管存儲器來存儲上述結構的實例。指向此結構的指針將傳遞給waveOut win32 API函數(waveOutPrepareHeader,waveOutWrite,waveOutUnprepareHeader)。

  1. 我應該用Marshal.AllocHGlobal()還是Marshal.AllocCoTaskMem()?有什麼不同?
  2. 我應該通過sizeof(WAVEHDR)Marshal.SizeOf(typeof(WAVEHDR))內存分配方法?有什麼不同?

請注意,分配的內存必須被固定。

回答

38

Windows程序總是至少有兩個分配非託管內存的堆。首先是Windows默認進程堆,當它需要代表程序分配內存時使用。第二個是COM基礎結構用於分配的堆。 .NET P/Invoke編組器假定這個堆被任何非託管代碼使用,其功能簽名需要取消分配內存。

AllocHGlobal從進程堆分配,AllocCoTaskMem從COM堆中分配。

每當您編寫非託管互操作代碼時,應始終避免分配非託管內存的代碼與釋放非託管內存的代碼不相同的情況。很有可能會使用錯誤的解除分配器。對於任何與C/C++程序進行交互的代碼而言,情況尤其如此。這些程序有自己的分配器,它使用自己的堆,由CRT在啓動時創建。在其他代碼中取消分配這樣的內存是不可能的,你不能可靠地得到堆句柄。這是P/Invoke問題的一個常見原因,尤其是因爲XP和更早版本中的HeapFree()函數默默地忽略了釋放未在正確堆中分配的內存的請求(泄漏分配的內存),但Vista和Win7崩潰程序有例外。

在你的情況下不需要擔心這個,你使用的mmsystem API函數是乾淨的。它們被設計爲確保分配的代碼也可以釋放。這是您必須調用waveInPrepareHeader()的一個原因,它會使用相同的代碼分配緩衝區,最終釋放緩衝區。可能與默認的進程堆。

您只需要分配WAVEHDR結構。當你完成它時,你有責任釋放它。 mmsystem API不會爲你做,最重要的是因爲它們不可靠。因此,你可以使用任何一個分配器,你只需要確保調用相應的自由方法。所有的Windows API都以這種方式工作。我使用CoTaskMemAlloc(),但確實沒有偏好。只是,如果我打電話設計得很差的代碼,使用COM堆稍微有點兒可能。

您不應該在互操作場景中使用sizeof()。它返回值類型的託管大小。在P/Invoke編組器根據[StructLayout]和[MarshalAs]指令翻譯結構類型之後,這可能就不一樣了。只有Marshal.SizeOf()爲您提供保證正確的值。


更新:VS2012發生了很大變化。現在包含的C運行時庫現在從默認進程堆分配,而不是使用自己的堆。長期來看,這使AllocHGlobal成爲最有可能成功的途徑。

+1

兩個分配函數之間是否存在任何性能差異? – DxCK 2011-01-15 15:25:21

+3

'AllocCoTaskMem'更高效。 'AllocHGlobal'調用'LocalAlloc',它有以下注意:「本地函數比其他內存管理函數具有更大的開銷並提供更少的功能。」請參閱https://msdn.microsoft.com/en-us/library/windows/desktop/aa366723(v=vs.85).aspx – IamIC 2017-08-11 09:15:20

2
1

2)據我知道sizeof只能與具有在編譯時預定義的大小的類型使用。因此使用Marshal.SizeOf(typeof(WAVEHDR))

相關問題