[英]How to P/Invoke a function that allocates and returns pointer to new array
我有一個C函數,我想從C#程序中調用。 函數壓縮輸入的字節數組並輸出新的壓縮數組。 看起來像這樣:
extern __declspec(dllexport) int Compress(chandle handle,
unsigned char *inputBuf, unsigned char **outputBuf, unsigned long *outputSize);
我已經將其翻譯成C#部分。 但是我得到的output
是只有一項的數組。
[DllImport("compresslib.dll", CallingConvention = CallingConvention.Cdecl)]
internal extern static int Compress(IntPtr handle, byte[] input, out byte[] output, out uint outputSize);
我應該怎么做才能使它正常工作?
這是我在Hans Passant的幫助下能夠編寫的工作代碼
[DllImport("compresslib.dll", CallingConvention = CallingConvention.Cdecl)]
internal extern static int Compress(IntPtr handle, byte[] input, out IntPtr output, out uint outputSize);
// and this is how i call it
byte[] outputData;
int outputDataSize;
IntPtr outputDataP = IntPtr.Zero;
try
{
int success = NativeMethods.Compress(handle,
inputData, out outputDataP, out outputDataSize);
if (success == -1)
{
throw new Exception("Compression failed.");
}
outputData = new byte[outputDataSize];
Marshal.Copy(outputDataP , outputData , 0, (int)outputDataSize);
}
finally
{
if (outputDataP != IntPtr.Zero)
NativeMethods.tjFree(outputDataP);// release unmanaged buffer
}
return outputData ;
..., unsigned char **outputBuf, ...
這個函數有一個非常嚴重的問題,它也不能從C程序可靠地調用。 調用方需要在使用后釋放輸出緩沖區。 這需要使用與C代碼中使用的分配器完全相同的分配器。 這在C程序中很難保證,而且經常會出錯。 就像DLL的用戶未使用與您使用的完全相同的編譯器版本一樣。 他將使用另一種版本的C運行時庫,該版本使用自己的堆。 因此,由於他沒有您使用的堆的句柄,因此無法釋放緩沖區。
當您單擊時,它幾乎變成不可能,CLR當然完全不知道您使用的是哪個C運行時版本,並且由於它具有自己的私有副本,因此保證不會使用與您使用的相同的C運行時版本。
您只得到一個字節的原因與此問題有關,pinvoke編組器不知道數組有多大,因為它沒有創建數組。 這是可修復的,您需要將[MarshalAs(UnmanagedType.LPArray),SizeParamIndex = 3]屬性應用於參數。 這告訴pinvoke編組,第4個參數包含數組的大小。
您將需要解決內存管理問題,因此無法解決問題,因為在pinvoke編組嘗試釋放陣列時,XP上的內存泄漏嚴重,而Vista或更高版本上的硬盤崩潰嚴重。 您需要更改C代碼以將內存用於分配給已知堆的返回輸出緩沖區。 這需要使用CoTaskMemAlloc() 。
另一個可能的解決方法是導出一個允許調用者釋放緩沖區的函數。 在這種情況下,您應該out IntPtr
聲明參數out IntPtr
並使用Marshal.Copy()編組自己,然后使用添加的函數釋放緩沖區。
或者使用一種完全不同的方式使用該函數,例如讓Compress()僅壓縮而不返回數據。 客戶端代碼可使用其他功能來發現所需的緩沖區大小並獲取數據副本。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.