C ++ / CLI - C#Interop - 字符串转换内存泄漏

时间:2016-08-12 18:10:05

标签: c# c++-cli interop

我需要将一些现有的.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转化。

我的问题很明显:如何实现字符串转换而不会泄漏。

谢谢!

3 个答案:

答案 0 :(得分:0)

您需要在使用指针后从StringToHGlobalUni释放指针。使用Marshal.FreeHGlobalLocalFree

答案 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*也会被删除。这意味着您不应该在使用后立即删除上下文,但是您需要提供足够的时间来调用代码来实际获取生成的字符串。