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

C#調用C++ DLL傳遞字符串參數:StringBuilder與內存管理避坑指南

     編輯:程式設計綜合問答

C#通過P/Invoke調用C++ DLL時,字符串參數(特別是輸出參數`char*`/`char[]`)的封送處理是最易出錯的環節。本文針對`CT_ReadPin`類接口,詳解`StringBuilder`正確用法、內存佈局對齊與常見錯誤。

C++ DLL 接口定義(假設)


// C++ 導出函數
// 讀取PIN碼到調用者提供的緩衝區
// 返回:0成功,非0錯誤碼
extern "C" __declspec(dllexport) LONG CT_ReadPin(
    int iPortNo, 
    char cExtendPort, 
    int iBaudRate, 
    int iVoiceType, 
    int iTimeOut, 
    char* pPinBuffer,   // 輸出緩衝區,調用者分配
    int iBufferSize     // 緩衝區大小
);

C# 正確簽名與調用


using System;
using System.Runtime.InteropServices;
using System.Text;

public class CentermWrapper
{
    // 1. 精確簽名:CallingConvention.Cdecl 必須與C++一致
    // CharSet.Ansi 對應 char*;若C++用wchar_t*則用 Unicode
    [DllImport("CT_InfoTerm.dll", EntryPoint = "CT_ReadPin", 
        CallingConvention = CallingConvention.Cdecl, 
        CharSet = CharSet.Ansi)]
    // 2. 輸出字符串參數用 StringBuilder,由CLR分配管理內存
    public static extern int CT_ReadPin(
        int iPortNo,
        char cExtendPort,      // 單字符按值傳遞
        int iBaudRate,
        int iVoiceType,
        int iTimeOut,
        StringBuilder pPinBuffer, // 輸出緩衝區
        int iBufferSize        // 緩衝區容量
    );

    public string ReadPin(int port, char extPort, int baud, int voiceType, int timeout)
    {
        // 3. 預分配足夠容量(如256),防止截斷
        var pinBuffer = new StringBuilder(256);
        
        int ret = CT_ReadPin(port, extPort, baud, voiceType, timeout, pinBuffer, pinBuffer.Capacity);
        
        if (ret != 0)
            throw new Exception($"DLL調用失敗,錯誤碼: {ret}");

        // 4. 直接獲取字符串,無需手動轉換
        return pinBuffer.ToString();
    }
}

關鍵避坑點

錯誤做法後果正確做法
用 `string` 接收輸出參數字符串不可變,無法接收DLL寫入的數據,可能導致內存訪問違規用 `StringBuilder` (可變字符緩衝區)
用 `byte[]` 或 `IntPtr` 手動分配易發生內存洩漏、釋放時機錯誤、編碼轉換麻煩`StringBuilder` 由CLR自動管理生命周期與編碼轉換
忽略 `CallingConvention`棧平衡錯誤,導致參數錯亂、崩潰(C++默認 `__cdecl`,StdCall需顯式指定)明確指定 `CallingConvention.Cdecl` 或 `StdCall`
忽略 `CharSet`默認Unicode(雙字節),與C++ `char*`(單字節)不匹配,內容亂碼或截斷指定 `CharSet.Ansi`
緩衝區容量不足DLL寫入超出邊界,導致緩衝區溢出、數據截斷預分配足夠大容量(如256、1024),傳入 `Capacity`

進階:結構體參數封送

若DLL接口涉及結構體,需用`[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]`定義對應C#類/結構體,字段順序、類型、大小完全一致。

調試技巧

  • 啟用非托管調試:項目屬性 → Debug → 勾選 `Enable unmanaged code debugging`。
  • 使用`DumpBin /exports CT_InfoTerm.dll`確認導出函數名、裝飾名。
  • 簡單測試:先寫C++控制台程序調用DLL驗證功能正常,再做C#封裝。
P/Invoke核心原則:簽名精確匹配、內存由CLR管理、編碼顯式指定、調用約定一致。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved