Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert C++/CLI string to const char*

Tags:

c++

c

c#

dll

I've got a C++/CLI DLL I plan to use as an adapter between my C# DLL and native C++ clients. I need to pass strings in both directions. The adapter is compiled with VS2013 but needs to support clients built with VS2008 so I'm using const char* in the API. But what I've got isn't working even when both are VS2013-built.

Elsewhere, I found advice to use msclr\marshal.h, so I created:

using namespace msclr::interop;
System::String^ ToCliString(const char* s)
{
    System::String ^result = marshal_as<System::String^>(s);
    return result;
}    
const char* ToCppString(System::String^ s)
{
    msclr::interop::marshal_context context;
    const char* result = context.marshal_as<const char*>(s);
    return result;
}

To test these, I created a round-trip conversion method in my C++/CLI DLL:

const char* Foo(const char *cstar)
{
    System::String^ cli = ::ToCliString(cstar);

    if (cli == "abc")
    {
        MessageBox::Show("const char* -> CLI: OK");
    }

    const char* cstar2 = ::ToCppString(cli);

    if (std::strcmp(cstar2, "abc") == 0)
    {
        MessageBox::Show("CLI -> const char*: OK");
    }
    else if (std::strcmp(cstar2, "") == 0)
    {
        MessageBox::Show("ToCppString returned empty string");
    }
    else
    {
        MessageBox::Show("ToCppString returned something else");
    }

    return cstar2;
}

When the native C++ client calls Foo("abc"), the 1st message gets displayed, the "returned something else" message gets displayed and the client receives junk (îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþÞ›vÚ§). So it would seem my 2nd conversion is not working.

UPDATE

Here's the final solution I used.

I used zneaks' advice to marshall to std::string and PaulMcKenzie's advice to pass a caller-allocated char* instead of returning const char*. Thank you both.

void ToCppString(System::String^ input, char* output)
{
    std::string temp= marshal_as<std::string>(input);
    strcpy(output, temp.c_str());
}

void Foo(const char* input, char* output)
{
    System::String^ cli = ::ToCliString(input);
    ::ToCppString(cli, output);
}
like image 620
Jim C Avatar asked Jan 14 '16 22:01

Jim C


1 Answers

The problem is that the marshal_context owns the char pointer that you got, so it is deallocated when your function returns:

This example creates a context for marshaling from a System::String to a const char * variable type.The converted data will not be valid after the line that deletes the context.

Consider using marshal_as<std::string> instead, since the string is allowed to outlive the marshal_context.

like image 154
zneak Avatar answered Oct 20 '22 12:10

zneak