[英]DllImport Returning null terminated string AccessViolationException
我正在嘗試與實現多個功能的 Dll 接口,其中一個采用 null 終止字符串和 int,並返回 null 終止字符串。 我試圖與這樣的方法進行交互:
[DllImport(dll_loc)]
[return : MarshalAs(UnmanagedType.LPStr)]
public static extern StringBuilder GetErrorMessage([MarshalAs(UnmanagedType.LPStr)]
StringBuilder message,
int error_code);
然后我嘗試像這樣調用該方法:
StringBuilder message = new StringBuilder(1000);
StringBuilder out2 = new StringBuilder(1000);
out2 = GetErrorMessage(message, res0);
但是,當我嘗試這樣做時,會拋出AccessViolationException
,告訴我我正在嘗試訪問受保護的 memory。
我成功地聲明了一種不同的方法:
[DllImport(dll_loc)]
public static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)]
StringBuilder version);
並以相同的方式調用它,但此方法不適用於當前的 function 調用。
我還嘗試返回一個 IntPtr,因為從技術上講,該方法返回一個指向字符串緩沖區第一個字符的指針,但無濟於事。
有沒有人知道這里可能出了什么問題? 這兩種方法之間可能有什么不同導致 dll 嘗試訪問它不應該訪問的 memory。 或者,你會如何建議調試這個問題?
返回字符串的 C 函數是 memory 管理問題。 C 字符串需要一個數組,並且該數組的 memory 需要在字符串被使用后釋放。 這使得這些功能很難在 C 程序中使用,幾乎不可能與 pinvoke 一起使用。 它也是一個經典的 C 錯誤,返回一個指向堆棧上的字符串的指針。
pinvoke marshaller 將嘗試釋放返回的字符串,以避免 memory 泄漏。 它將使用 CoTaskMemFree()。 這通常不會有好的結果,C 代碼實際上使用 CoTaskMemAlloc 為數組分配 memory 的情況很少見。 在 XP 上,這將導致 memory 無聲泄漏。 Vista 和 Win7 有更嚴格的堆管理器,如果附加了非托管調試器,它們將調用調試中斷。 然后用 AccessViolation 轟炸程序。
您可以通過將返回類型聲明為 IntPtr 並自己編組字符串來避免自動編組行為。 通常使用 Marshal.PtrToStringAnsi()。 但是您仍然面臨着發布 memory 的任務。 你不能,你沒有 CRT 堆的句柄,你不能調用 free()。
應使用傳遞字符串緩沖區的參數聲明返回字符串的 C 函數。 還有一個參數說明緩沖區有多大。 像這樣:
int GetErrorMessage(int errorCode, char* buffer, size_t bufferSize);
現在很簡單,調用者可以為緩沖區分配 memory 並釋放它。 C function 只是將字符串復制到緩沖區中。 返回值可用於告知復制了多少字符,或指示需要更大的緩沖區。 不要忽略bufferSize
參數,緩沖區溢出是致命的,並會調用可怕的 FatalExecutionEngineError 異常,該異常與 AV 一樣不可調試,因為直到很久以后才檢測到 GC 堆損壞。 您在 C# 端使用 StringBuilder,並使用非零容量適當地初始化。 緩沖區大小的值。
將所有StringBuilder
替換為System.IntPtr
,僅使用System.Runtime.InteropServices.Marshal.AllocHGlobal
分配message
變量
然后使用PtrToStringAuto
將message
或out2
轉換為字符串
然后調用System.Runtime.InteropServices.Marshal.FreeHGlobal
在處理char*
或wchar_t*
時,總是以這種方式為我工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.