Marshal std :: string of System :: String ^ member of struct handle

I am trying to marshal a std :: string from System :: String ^.

This can usually be done using the marshal_as<T>

ie template

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

      

But if String ^ is part of the struct referenced ie

value struct SomeStruct {
    String ^managedString;
};

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

      

The compiler throws the following error

error C2665: 'msclr :: interop :: marshal_as': none of the three overloads can convert all argument types

However, if the structure is not a descriptor, but an actual structure, it works great.

+3


source to share


1 answer


   auto structHandle = gcnew SomeStruct();

      

This is where the problem started. The structHandle link points to a boxed copy of SomeStruct. It should be boxed because SomeStruct is a value type, a copy of the structure is kept on the GC heap.

The signature of the marshal_as <> overload you are trying to use is as follows:

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

      

Problem const&

- you cannot get an unmanaged member reference, its address is unstable (not const) because the garbage collector can move the object while marshal_as <> is being executed. This can lead to disaster when marshal_as now locks on an object that no longer exists.

A workaround is to copy the reference from the boxed object before trying to convert it:



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

      

But this is just a hack for the real problem in your code. There are value types to make code efficient by allowing structs to be stored in a stack frame or CPU register. Just like a native C ++ object. You give up this advantage by boxing the structure. Or, in other words, it just didn't make sense to declare value struct

unless you're going to treat it as a value. Use it correctly for the correct fix:

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

      

Or, if you got a reference by mistakenly using ^ on a function argument, rewrite it to:

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

      

Note the use of% instead of ^ to pass a variable by reference. But don't do that, the point of value type is that it is cheaper to copy the value than to be able to dereference the pointer to get the value. Make sure your value type is not too large, it should contain no more than 4 fields. If it is larger, you must use a reference type.

+3


source







All Articles