我需要将一些现有的.NET逻辑(即程序集MyManaged.dll)暴露给本机代码,因此我决定创建C ++ / CLI bridge 。我创建了C ++ / CLI项目并添加了对MyManaged.dll的引用。简而言之 - 它有效 - 我已经成功访问了应该可以从本机代码访问的所有内容。
但大问题是我的实现泄漏了内存。经过几天的测试和研究,我已将问题缩小到System::String
< - > const wchar_t
次转化。最后,我创建了一个简单的C ++ / CLI项目,演示(重现)该问题:
#define EXPORTED __declspec(dllexport)
System::String^ ToManaged(const wchar_t* unmanagedString)
{
return gcnew System::String(unmanagedString);
}
const wchar_t* ToUnmanaged(System::String^ managedString)
{
return (wchar_t*) System::Runtime::InteropServices::Marshal::StringToHGlobalUni(managedString).ToPointer();
}
EXPORTED const wchar_t* __stdcall GetString(const wchar_t* dummy)
{
return ToUnmanaged(ToManaged(dummy));
}
(如果从前面的代码中看不出来 - 我在C ++ / CLI中很新)
正如我所提到的,代码可以工作,但累积了内存消耗,因此System::String
< - >中肯定存在泄漏。 const wchar_t
转化。
我的问题很明显:如何实现字符串转换而不会泄漏。
谢谢!
答案 0 :(得分:0)
您需要在使用指针后从StringToHGlobalUni
释放指针。使用Marshal.FreeHGlobal
或LocalFree
。
答案 1 :(得分:0)
虽然没有根据您的确切api构建,但我认为这解决了内存
c:choose
答案 2 :(得分:0)
更新:请忽略负面选民 - 你可以看到他甚至拒绝解释这里的错误。不同的人有不同的动机......有一件事是肯定的:这里提供的解决方案完美无缺,没有任何内存泄漏。
我找到了解决方案(基于Overview of Marshaling in C++和marshal_context::marshal_as)。所以应该改变以下内容:
#include <msclr\marshal.h>
System::String^ ToManaged(const wchar_t* unmanagedString)
{
return msclr::interop::marshal_as<System::String^>(unmanagedString);
}
gcroot<msclr::interop::marshal_context^> context;
const wchar_t* ToUnmanaged(System::String^ managedString)
{
msclr::interop::marshal_context^ unpacked = context;
if (unpacked != nullptr)
delete unpacked;
context = gcnew msclr::interop::marshal_context();
return context->marshal_as<const wchar_t*>(managedString);
}
注意:这里我实施了对marshal_context
实例非常笨拙的处理 - 当下一个呼叫到达时,前一个呼叫的结果将被删除。这种实现在多线程场景中会分崩离析,因此您应该实现一个更好的方案,并考虑以下因素:
marshal_context
实例可用于多个调用,但应该不时删除它(以便从以前编组的字符串中释放内存); marshal_context
后,所有使用它的const wchar_t*
也会被删除。这意味着您不应该在使用后立即删除上下文,但是您需要提供足够的时间来调用代码来实际获取生成的字符串。