首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

C# DllImport 調用 C++ DLL 發生 0xc0000005 訪問違規:String 封送處理錯誤分析與修復

     編輯:程式設計疑難解答

錯誤現象

錯誤地址:0x66fab3fc,線程:0x31b8,錯誤代碼:0xc0000005
此錯誤可能是 CLR 中的 bug,或者是用戶代碼的不安全部分中的 bug。
常見來源:COM-interop 或 PInvoke 的封送處理錯誤,可能損壞堆棧。

問題代碼

// C++ 聲明
int __stdcall Dcrf_ReadCard(char *S); // 讀卡

// C# 錯誤聲明
[DllImport("LsdDcrf.dll", CallingConvention = CallingConvention.StdCall)]
public extern static int Dcrf_ReadCard(ref String Str);

// 調用代碼
String str = "";
CardRead.Dcrf_ReadCard(ref str);
lab.Text = str.ToString();

核心問題:String 封送處理機制

C# String 是不可變的引用類型,默認封送為 LPWStr(雙字節寬字符指針),而 C++ 期望的是 char*(單字節 ANSI 指針)。

當使用 ref String 時:

  1. CLR 分配一個新的字符串緩衝區
  2. 將指針傳遞給非托管代碼
  3. 非托管代碼寫入數據
  4. CLR 嘗試將非托管內存轉換為托管 String

由於編碼不匹配(ANSI vs Unicode)且非托管代碼可能寫入超出預期長度,導致堆棧損壞,觸發 0xc0000005 訪問違規。

正確解決方案

方案一:使用 StringBuilder(推薦,適用於輸出字符串)

[DllImport("LsdDcrf.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int Dcrf_ReadCard(StringBuilder sb);

// 調用
StringBuilder sb = new StringBuilder(256); // 預分配足夠緩衝區
int result = Dcrf_ReadCard(sb);
lab.Text = sb.ToString();

方案二:使用 byte[] 或 IntPtr 手動處理(最可控)

[DllImport("LsdDcrf.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int Dcrf_ReadCard(byte[] buffer, int bufferSize);

// 調用
byte[] buffer = new byte[256];
int result = Dcrf_ReadCard(buffer, buffer.Length);
string str = System.Text.Encoding.Default.GetString(buffer).TrimEnd('\0');
lab.Text = str;

方案三:修正原聲明(最小改動)

[DllImport("LsdDcrf.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int Dcrf_ReadCard([Out] StringBuilder sb);
// 或
[DllImport("LsdDcrf.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int Dcrf_ReadCard(out string str); // 需 C++ 端配合 CoTaskMemAlloc

關鍵屬性說明

屬性作用
CharSet = CharSet.Ansi指定封送為單字節 ANSI,匹配 C++ char*
[Out]標記為輸出參數,CLR 只在返回時封送回托管端
StringBuilder可變緩衝區,安全接收非托管寫入

調試建議

  1. 啟用 非托管調試:項目屬性 → Debug → Enable unmanaged code debugging
  2. 使用 Marshal.PtrToStringAnsi 手動轉換 IntPtr 驗證數據
  3. 檢查 C++ 端是否正確處理緩衝區長度,避免緩衝區溢出

總結

永遠不要用 `ref string` 接收非托管輸出字符串。改用 StringBuilder + CharSet.Ansi 是最穩健、最符合 .NET 互操作規範的做法。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved