.NET调用DllImport函数时出现malloc双重释放问题求助
问题根源与解决方案
这个双重释放的问题,核心原因是C#封送器对返回字符串的默认处理逻辑和C库的内存管理规则不匹配:
为什么会出错?
你的C函数confd_ns2prefix返回的是指向C库内部维护的内存(schema->prefix)的指针,这块内存的生命周期完全由C库自己管控,不需要调用者释放。但当你在C#里声明返回值为string时,CLR会默认认为这个字符串是通过CoTaskMemAlloc分配的,因此每次调用结束后,都会自动调用CoTaskMemFree去释放返回的指针。
第一次调用后,CLR已经释放了0x7f8182037b20这块内存;第二次调用时,C函数返回了同一个指针(因为对应的schema还在),CLR再次尝试释放已经被释放的内存,就触发了双重释放错误。
怎么解决?
你需要绕过CLR的自动释放逻辑,改用IntPtr接收返回指针,再手动转换成C#字符串,具体步骤如下:
- 修改DllImport声明,把返回类型从
string改成IntPtr:
[DllImport("libconfd.so", SetLastError = true)] public static extern IntPtr confd_ns2prefix(UInt32 ns);
- 调用函数并转换字符串,使用
Marshal.PtrToStringAnsi(对应C的char*ANSI字符串,如果你知道是UTF-8编码,.NET Core 3.0+可以用Marshal.PtrToStringUTF8):
// 第一次调用 IntPtr prefixPtr = libconfd.confd_ns2prefix((uint)1826703833); if (prefixPtr != IntPtr.Zero) { string prefix = Marshal.PtrToStringAnsi(prefixPtr); Console.WriteLine(prefix); } // 第二次调用,完全不会触发释放问题 prefixPtr = libconfd.confd_ns2prefix((uint)1826703833); if (prefixPtr != IntPtr.Zero) { string prefix = Marshal.PtrToStringAnsi(prefixPtr); Console.WriteLine(prefix); }
- 重要注意事项:
- 绝对不要手动调用
Marshal.FreeHGlobal或Marshal.FreeCoTaskMem去释放这个IntPtr,因为内存归C库所有,释放权在C库手里。 Marshal.PtrToStringAnsi会创建一个C#字符串的副本,所以即使后续C库修改了原内存,你的C#字符串也不受影响,安全性更高。
- 绝对不要手动调用
内容的提问来源于stack exchange,提问作者bmigette
相关产品推荐
相关产品推荐

