You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#调用C++ DLL输出char*时内存泄漏问题及正确封送方案咨询

问题根因
  • 缓冲区溢出(核心崩溃原因):你在C#中初始化StringBuilder时没有指定预分配容量,默认初始容量仅为16。C++侧的sprintf不会校验目标缓冲区长度,只要返回的属性值长度超过16,就会直接越界写入,破坏托管堆内存结构,高频调用下很快会触发内存访问错误导致崩溃。
  • C++函数存在安全缺陷:现有C++函数没有提供缓冲区长度入参,无法感知外部传入的oAttValue可用空间大小,天生存在越界写入风险。
  • 补充:C侧MEM_free是C风格内存释放接口,不会抛出C异常,包裹的try-catch逻辑完全无效,属于冗余代码。
临时修复方案(不改动C++ DLL)

先确认业务中返回的属性值最大长度,初始化StringBuilder时指定足够大的容量即可,示例修改后的C#代码如下:

[DllImport("tc_custom_caller.dll", EntryPoint = "get_value_of_field", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern int get_value_of_field(int iObjectTag,
                                             [In, MarshalAs(UnmanagedType.LPStr)]string iAttName,
                                             StringBuilder oAttValue);

public static void Get_Value_Of_Field(int iObjectTag, string iAttName, out string oAttValue)
{
    oAttValue = "";
    // 按业务实际最大长度设置,比如确认最长不超过2048就设2048,不确定可以设为4096
    StringBuilder sbValue = new StringBuilder(4096);
    int retcode = get_value_of_field(iObjectTag, iAttName, sbValue);
    if (retcode != 0) 
    { 
        throw new Exception("Error in get_value_of_field: " + Get_Error_Text(retcode)); 
    }
    oAttValue = sbValue.ToString();
}
更合理的封送优化方案(建议改动C++接口)

方案1:增加缓冲区长度校验(最小改动)

修改C++导出函数,新增缓冲区长度入参,改用snprintf做边界检查:

// 修改后的C++函数
int get_value_of_field ( const int iObjectTag,
                         const char iAttName[],
                         char *oAttValue,
                         const int iBufLen) // 新增:传入缓冲区可用长度
{
    int retcode = 0;
    char *AttValue = NULL;
    ITK (tc_custom_methods::get_value_of_field (iObjectTag, iAttName, &AttValue));
    if (retcode == 0 && oAttValue != NULL && iBufLen > 0) 
    { 
        // 最多写入iBufLen-1个字符,避免溢出
        snprintf(oAttValue, iBufLen, "%s", AttValue); 
    }
    if (AttValue != NULL) MEM_free(AttValue);
    return retcode;
}

对应修改C#的DllImport定义,传StringBuilder的容量进去即可。

方案2:直接返回BSTR(最省心,无需手动处理内存)

如果允许调整接口返回值,C++侧直接返回BSTR类型,C#侧可以直接接收string,由.NET运行时自动释放内存,完全避免缓冲区溢出问题:

// 修改后的C++导出函数
BSTR __stdcall get_value_of_field (const int iObjectTag, const char iAttName[], int* oRetCode)
{
    *oRetCode = 0;
    char *AttValue = NULL;
    ITK (tc_custom_methods::get_value_of_field (iObjectTag, iAttName, &AttValue));
    BSTR res = NULL;
    if (*oRetCode == 0 && AttValue != NULL)
    {
        res = SysAllocStringByteLen(AttValue, strlen(AttValue));
    }
    if (AttValue != NULL) MEM_free(AttValue);
    return res;
}

对应C#导入逻辑:

[DllImport("tc_custom_caller.dll", EntryPoint = "get_value_of_field", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string get_value_of_field(int iObjectTag, string iAttName, out int oRetCode);

public static string Get_Value_Of_Field(int iObjectTag, string iAttName)
{
    int retCode = 0;
    string res = get_value_of_field(iObjectTag, iAttName, out retCode);
    if (retCode != 0)
    {
        throw new Exception("Error in get_value_of_field: " + Get_Error_Text(retCode));
    }
    return res;
}

内容的提问来源于stack exchange,提问作者kor6k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 08:54:04