跨Windows与Linux平台,释放C#分配、C++接收的字符串内存的实现方案
跨Windows与Linux平台,释放C#分配、C++接收的字符串内存的实现方案
我来给你梳理两个靠谱的跨平台解决方案,完美解决你现在的问题——毕竟跨平台的时候,内存分配释放的坑确实不少,稍不注意就会搞出内存泄漏或者崩溃。
首先得先搞懂核心问题:你用[return: MarshalAs(UnmanagedType.LPWStr)]让C#返回字符串时,CLR会把托管字符串复制到非托管内存里,但这个内存的分配方式在Windows和Linux上是不一样的:
- Windows上,CLR用的是
CoTaskMemAlloc(和GlobalFree兼容,但更推荐用对应的CoTaskMemFree释放) - Linux上,.NET Core/.NET 5+用的是标准C库的
malloc来分配内存
所以直接在C++里硬编码GlobalFree肯定不行,得搞跨平台适配,下面两个方案任选其一:
方案一:让C#提供专属释放函数(最推荐,零平台适配成本)
这个方案最省心,分配和释放全交给CLR处理,你不用管底层是CoTaskMemAlloc还是malloc,完全跨平台。
具体实现
- C#端添加释放函数
在你的C#类里加一个静态方法,专门用来释放之前返回的字符串内存:
using System.Runtime.InteropServices; public static class YourInteropClass { [return: MarshalAs(UnmanagedType.LPWStr)] public static string? GetText() { return SomeClass.SomeMethodThatReturnsAString(); } // 新增的释放函数 [UnmanagedCallersOnly] // .NET Core 3.0+/NET 5+推荐用这个,.NET Framework可改用DllExport public static void FreeText(IntPtr stringPtr) { Marshal.FreeCoTaskMem(stringPtr); } }
注意:如果是.NET Framework环境,可能需要借助第三方的DllExport库来暴露函数;.NET Core及以上版本用
[UnmanagedCallersOnly]就能直接把函数暴露给C++调用。
- C++端调用释放函数
修改你的C++代码,调用C#提供的FreeText来释放,而不是自己写平台相关的逻辑:
// 先声明C#暴露的释放函数 extern "C" void FreeText(void* stringPtr); void OnWriteValue() { char16_t const *string = GetText(); if (string) { someMethodThatPrints(string); // 把char16_t*转成void*传给C#的释放函数 FreeText(const_cast<char16_t*>(string)); } }
这个方案的好处就是完全不用关心平台差异,CLR自己会处理Windows和Linux上的释放逻辑,绝对不会出错。
方案二:C++端做平台适配,直接释放(适合不想改C#代码的场景)
如果不想改动C#的代码,那可以在C++里通过预编译指令区分平台,调用对应的释放函数:
具体实现
#include <cstddef> // 包含nullptr定义 #ifdef _WIN32 #include <combaseapi.h> // Windows下包含CoTaskMemFree的头文件 #else #include <cstdlib> // Linux下包含free的头文件 #endif void freeMemoryThatIsAllocatedInCSharp(char16_t* ptr) { if (ptr == nullptr) return; // 空指针直接返回,避免崩溃 #ifdef _WIN32 // Windows用CoTaskMemFree,和CLR分配的CoTaskMemAlloc对应 CoTaskMemFree(ptr); #else // Linux用free,和CLR的malloc分配对应 free(ptr); #endif }
然后你的C++代码直接调用这个函数就行:
void OnWriteValue() { char16_t const *string = GetText(); if (string) { someMethodThatPrints(string); freeMemoryThatIsAllocatedInCSharp(const_cast<char16_t*>(string)); } }
注意事项
- 别混用释放函数:比如Windows上不能用
free来释放CLR分配的内存,Linux上也不能用CoTaskMemFree(系统根本没有这个函数) - 如果你的C#代码是手动用
Marshal.StringToHGlobalUni分配的内存,那Windows上要对应用GlobalFree释放;这时候方案一依然适用,只需要把C#的Marshal.FreeCoTaskMem改成Marshal.FreeHGlobal就行
内容来源于stack exchange
相关产品推荐
相关产品推荐

