簡體   English   中英

DllImport 返回 null 終止的字符串 AccessViolationException

[英]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變量

然后使用PtrToStringAutomessageout2轉換為字符串

然后調用System.Runtime.InteropServices.Marshal.FreeHGlobal

在處理char*wchar_t*時,總是以這種方式為我工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM