简体   繁体   English

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

[英]C++/CLI - C# Interop - string conversion memory leak

I need to expose some existing .NET logic (ie assembly MyManaged.dll) to native code, so I've decided to create C++/CLI bridge . 我需要将一些现有的.NET逻辑(即汇编MyManaged.dll)暴露给本机代码,所以我决定创建C ++ / CLI I've created C++/CLI project and added an reference to MyManaged.dll. 我创建了C ++ / CLI项目并添加了对MyManaged.dll的引用。 Long story in short - it works - I've succeeded to access everything that should be accessible form native code. 简而言之 - 它很有效 - 我已经成功访问​​了应该可以从本机代码访问的所有内容。

But big issue is that my implementation leaks memory. 但是大问题是我的实现泄漏了内存。 After days of testing and researching I've narrowed problem down to System::String <-> const wchar_t conversion. 经过几天的测试和研究,我已经将问题缩小到System::String < - > const wchar_t转换。 Finally I've created a trivial C++/CLI project that demonstrates (reproduces) the issue: 最后,我创建了一个简单的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));
}

(If it isn't obvious from the previous code - I'm pretty new in C++/CLI) (如果从前面的代码中看不出来 - 我在C ++ / CLI中很新)

As I've mentioned the code works but accumulates memory consumption so there's definitely a leak in System::String <-> const wchar_t conversion. 正如我所提到的,代码可以工作,但会累积内存消耗,所以在System::String < - > const wchar_t转换中肯定存在泄漏。

My question is obvious: how to implement string conversion without the leak. 我的问题很明显:如何在没有泄漏的情况下实现字符串转换。

Thanks! 谢谢!

You need to free the pointer from StringToHGlobalUni after you've used it. 使用它之后,需要从StringToHGlobalUni释放指针。 Use Marshal.FreeHGlobal or LocalFree . 使用Marshal.FreeHGlobalLocalFree

While not built to your exact api, I think this addresses the memory 虽然不是根据您的确切api构建的,但我认为这可以解决内存问题

HRESULT GetString(BSTR* p_bstrResult, unsigned long* ulErrCode)
{
    HRESULT hr = S_OK;

    try
    {
        System::String ^systemstring = gcnew System::String("");
                    DotNetObject::o = gcnew  DotNetObject:: DotNetObjectComponent();
        *ulErrCode = (unsigned long)o->GetString(systemstring); 
        pin_ptr<const wchar_t> wch = PtrToStringChars(systemstring);
        _bstr_t bstrt(wch);
        *p_bstrResult = bstrt.GetBSTR(); // native client babysits
        delete systemstring;        
    }
    catch(Exception ^ ex)
    {

    }   
    return hr;

}

UPDATE : Please ignore negative voter - as you can see he even refused to explain what is wrong here. 更新 :请忽略负面选民 - 你可以看到他甚至拒绝解释这里有什么问题。 Different people have different motives... One thing is for sure: solution provided here works perfectly, without any memory leak. 不同的人有不同的动机......有一件事是肯定的: 这里提供的解决方案完美无缺,没有任何内存泄漏。

I've found the solution (based on Overview of Marshaling in C++ and marshal_context::marshal_as ). 我找到了解决方案(基于C ++中的Marshaling概述marshal_context :: marshal_as )。 So the following should be changed: 所以应该改变以下内容:

#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);
}

NOTE: Here I've implemented very clumsy handling of marshal_context instance - when the next call arrives, the result from the previous call is deleted. 注意:这里我实现了对marshal_context实例的非常笨拙的处理 - 当下一个调用到达时,前一个调用的结果被删除。 This implementation would fall apart in multi-threading scenario, so you should implement a better one having the following in mind: 这种实现在多线程场景中会分崩离析,因此您应该实现一个更好的方案,并考虑以下因素:

  • marshal_context instance can be used for multiple calls, but it should be deleted every now and then (to free the memory from previously marshaled strings); marshal_context实例可用于多个调用,但应该marshal_context地删除它(以便从以前封送的字符串中释放内存);
  • As soon as marshal_context is deleted - all const wchar_t* crated by using it are also deleted. 删除marshal_context ,所有使用它的const wchar_t*也会被删除。 It means that you should not delete context immediately after using it, but you need to provide enough time for calling code to actually get the resulting string. 这意味着您不应该在使用后立即删除上下文,但是您需要提供足够的时间来调用代码来实际获取结果字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM