c++ cli - c++ / cli - c#互操作-字符串转换内存泄漏

本文关键字:c++ cli 内存 泄漏 转换 互操作 字符串 | 更新日期: 2023-09-27 18:02:16

我需要将一些现有的。net逻辑(即汇编MyManaged.dll)暴露给本机代码,因此我决定创建c++/CLI 。我已经创建了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转换中肯定有泄漏。

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

谢谢!

c++ cli - c++ / cli - c#互操作-字符串转换内存泄漏

更新:请忽略负面选民-正如你所看到的,他甚至拒绝解释这里的问题。不同的人有不同的动机……有一件事是肯定的:这里提供的解决方案工作完美,没有任何内存泄漏。

我找到了解决方案(基于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*也被删除。这意味着您不应该在使用上下文后立即删除它,但您需要提供足够的时间来调用代码以实际获得结果字符串。

您需要在使用StringToHGlobalUni之后释放它。使用Marshal.FreeHGlobalLocalFree

虽然没有构建到您的确切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;
}