从结构句柄的System :: String ^成员Marshal到std :: string

时间:2015-05-29 12:39:20

标签: .net c++-cli marshalling

我正在尝试从System :: String ^。

编组一个std :: string

通常可以使用marshal_as<T>模板,即

来完成
System::String ^managedString = "test";
std::string stdString = marshal_as<std::string>(managedString);

但是如果String ^是通过引用访问的结构的一部分,即

value struct SomeStruct {
    String ^managedString;
};

auto structHandle = gcnew SomeStruct();
structHandle->managedString = "test";
std::string stdString = marshal_as<std::string>(structHandle->managedString);

编译器将抛出以下错误

  

错误C2665:&#39; msclr :: interop :: marshal_as&#39; :3个重载都没有   可以转换所有参数类型

但是,如果struct不是句柄,而是实际的struct,它可以正常工作。

1 个答案:

答案 0 :(得分:3)

   auto structHandle = gcnew SomeStruct();

这是问题的开始。 structHandle 引用指向SomeStruct的盒装副本。它需要装箱,因为SomeStruct是一个值类型,结构的副本存储在GC堆上。

marshal_as&lt;&gt;的签名你试图使用的超载是:

   marshal_as<std::string,System::String^>(System::String ^const &)

const&是问题,你不能获得对成员的非托管引用,它的地址不稳定(不是const),因为垃圾收集器可以移动对象而marshal_as&lt;&gt;正在执行。当marshal_as现在取消引用不再存在的对象时,这将导致灾难。

解决方法是在尝试转换之前将引用复制到盒装对象之外:

   structHandle->managedString = "test";
   String^ refCopy = structHandle->managedString;
   std::string stdString = marshal_as<std::string>(refCopy);   // fine

但这只是对代码中真正问题的破解。存在值类型以使代码有效,允许结构存储在堆栈帧或CPU寄存器中。就像本机C ++对象一样。你通过拳击结构扔掉了这个优势。或者换句话说,如果你不打算将它视为一个值,那么宣布value struct就没有任何意义。正确使用它以获得正确的修复:

    SomeStruct value;
    value.managedString = "test";
    auto result = marshal_as<std::string>(value.managedString);  // fine

或者如果你在函数参数上错误地使用了^而得到了引用,那么重写为:

    void SomeFunction(SomeStruct% arg) {
        auto nativeString = marshal_as<std::string>(arg.managedString);
        '' etc..
    }

注意使用%而不是^来通过引用传递变量。但是不要这样做,值类型的要点是复制值比不必取消引用指针以获取值更便宜。确保您的值类型不是太大,它不应超过4个字段。如果它更大,那么你应该使用引用类型。