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

C++/CLI-C#互操作-字符串转换内存泄漏,c#,c++-cli,interop,C#,C++ Cli,Interop,我需要将一些现有的.NET逻辑(即assembly MyManaged.dll)公开给本机代码,因此我决定创建C++/CLI桥。我已经创建了C++/CLI项目,并添加了对MyManaged.dll的引用。长话短说——它起作用了——我已经成功地访问了所有应该从本机代码中访问的内容 但是最大的问题是我的实现泄漏内存。经过几天的测试和研究,我已经把问题缩小到System::Stringconstwchar\t转换。最后,我创建了一个简单的C++/CLI项目,它演示(再现)了这个问题: #define

我需要将一些现有的.NET逻辑(即assembly MyManaged.dll)公开给本机代码,因此我决定创建C++/CLI桥。我已经创建了C++/CLI项目,并添加了对MyManaged.dll的引用。长话短说——它起作用了——我已经成功地访问了所有应该从本机代码中访问的内容

但是最大的问题是我的实现泄漏内存。经过几天的测试和研究,我已经把问题缩小到
System::String
constwchar\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\u t
转换中肯定存在漏洞

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


谢谢

使用后,需要将指针从
StringToHGlobalUni
中释放出来。使用
Marshal.FreeHGlobal
LocalFree

使用后,需要将指针从
StringToHGlobalUni
中释放出来。使用
Marshal.FreeHGlobal
LocalFree

虽然不是按照您的确切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;

}
HRESULT GetString(BSTR*p_bstrResult,无符号长*ulErrCode)
{
HRESULT hr=S_正常;
尝试
{
System::String^systemstring=gcnew System::String(“”);
DotNetObject::o=gcnew DotNetObject::DotNetObjectComponent();
*ulErrCode=(无符号长)o->GetString(系统字符串);
pin_ptr wch=PtrToStringChars(系统字符串);
_bstr_t bstrt(wch);
*p_bstrResult=bstrt.GetBSTR();//本地客户端保姆
删除系统字符串;
}
捕获(异常^ex)
{
}   
返回人力资源;
}

虽然不是按照您的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;

}
HRESULT GetString(BSTR*p_bstrResult,无符号长*ulErrCode)
{
HRESULT hr=S_正常;
尝试
{
System::String^systemstring=gcnew System::String(“”);
DotNetObject::o=gcnew DotNetObject::DotNetObjectComponent();
*ulErrCode=(无符号长)o->GetString(系统字符串);
pin_ptr wch=PtrToStringChars(系统字符串);
_bstr_t bstrt(wch);
*p_bstrResult=bstrt.GetBSTR();//本地客户端保姆
删除系统字符串;
}
捕获(异常^ex)
{
}   
返回人力资源;
}

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

我已经找到了解决方案(基于和)。因此,应更改以下内容:

#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);
}
#包括
系统::字符串^ToManaged(常量wchar\u t*非托管字符串)
{
返回msclr::interop::marshal_as(非托管字符串);
}
根上下文;
const wchar_t*ToUnmanaged(系统::字符串^managedString)
{        
msclr::interop::封送上下文^unpacket=context;
如果(未打包!=nullptr)
删除未包装的文件;
context=gcnew msclr::interop::marshal_context();
返回上下文->封送(managedString);
}
注意:我在这里实现了非常笨拙的
封送上下文
实例处理-当下一个调用到达时,上一个调用的结果将被删除。此实现在多线程场景中可能会崩溃,因此您应该实现一个更好的实现,并牢记以下几点:

  • marshal_context
    实例可用于多个调用,但应不时将其删除(以从以前封送的字符串中释放内存)
  • 一旦
    marshal_context
    被删除,所有使用它装箱的
    const wchar_t*
    也将被删除。这意味着您不应该在使用上下文后立即删除它,但您需要为调用代码提供足够的时间来实际获取结果字符串

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

我已经找到了解决方案(基于和)。因此,应更改以下内容:

#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);
}
#包括
系统::字符串^ToManaged(常量wchar\u t*非托管字符串)
{
返回msclr::interop::marshal_as(非托管字符串);
}
根上下文;
const wchar_t*ToUnmanaged(系统::字符串^managedString)
{        
msclr::interop::封送上下文^unpacket=context;
如果(未打包!=nullptr)
删除未包装的文件;
context=gcnew msclr::interop::marshal_context();
返回上下文->封送(managedString);
}
注意:我在这里实现了非常笨拙的
封送上下文
实例处理-当下一个调用到达时,上一个调用的结果将被删除。此实现在多线程场景中可能会崩溃,因此您应该实现一个更好的实现,并牢记以下几点:

  • marshal_context
    实例可用于多个调用,但应不时将其删除(以从以前封送的字符串中释放内存)
  • 一旦
    marshal_context
    被删除,所有使用它装箱的
    const wchar_t*
    也将被删除。这意味着您不应该在使用上下文后立即删除它,但您需要为调用代码提供足够的时间来实际获取结果字符串

感谢您的帮助!但这并没有帮助(我已经试过了)。更准确地说:一次