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

C#互操作中char*转string触发堆损坏异常的原因排查

C#封送C结构体char*为string触发堆损坏异常的原因分析

在用[MarshalAs(UnmanagedType.LPStr)]将C结构体中的char*封送到C#的string类型时,触发了“堆已损坏”异常,但C# string转C char*的反向操作正常。改用IntPtr接收char*则能正常运行,以下是具体细节及原因解析:

C端结构体与函数签名

struct STRUCTV
{
    char* _pretext;
    char* _result;
};

函数签名:void add_all_numbers(STRUCTV *structV);

触发异常的C#代码

结构体定义

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct STRUCTV
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string _pretext; 
    [MarshalAs(UnmanagedType.LPStr)]
    public string _result;  // 此处无法正常工作
}

导入Native函数

internal partial class NativeMethods
{
    [DllImport(
    "Library.dll",
    EntryPoint = "function1",
    CallingConvention = CallingConvention.Cdecl,
    CharSet = CharSet.Ansi)]
    public static extern void function1(ref STRUCTV structV);
}

调用方式

STRUCTV myStruct = new STRUCTV();
myStruct._pretext = "Pretext";
myStruct._result = null;
NativeMethods.function1(ref myStruct); // 触发ntdll.dll : A heap has been corrupted异常
Console.WriteLine(myStruct._result);

C端内部逻辑

string resultstring = GetResult();
char* result = new char[resultstring.length() + 1];
strcpy_s(result, resultstring.length() + 1, resultstring.c_str());
structV->_result = result;

正常运行的C#代码(使用IntPtr)

结构体定义

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct STRUCTV
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string _pretext; 
    public IntPtr _result;
}

调用方式

STRUCTV myStruct = new STRUCTV();
myStruct._pretext = "Pretext";
myStruct._result = IntPtr.Zero;
NativeMethods.function1(ref myStruct);
string resultString = Marshal.PtrToStringAnsi(myStruct._result);
Console.WriteLine(resultString);    

原因解析

  • 当C#将结构体中的string标记为[MarshalAs(UnmanagedType.LPStr)]时,CLR默认认为该字符串的内存由托管堆分配,在封送完成后会尝试用CoTaskMemFree释放指针指向的内存。
  • 但C端代码是用C++的new(CRT堆)分配的内存,CLR用CoTaskMemFree释放CRT堆内存会导致堆损坏——不同堆的分配/释放机制不兼容。
  • 改用IntPtr时,CLR不会自动释放该指针指向的内存,由开发者手动处理内存生命周期(若需释放,需在C端提供对应的释放函数,比如void free_result(STRUCTV* structV) { delete[] structV->_result; },再在C#调用完后执行该函数),因此不会触发堆损坏异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 16:43:17