0

I have this tutorial working: https://www.red-gate.com/simple-talk/dotnet/net-development/creating-ccli-wrapper/

That tutorial uses 3 Visual Studio projects in one Solution. The "Core" project is the native C++ side. The "Wrapper" project is the C++/CLI "bridge". And the "Sandbox" project is the C# side.

Now I am trying to modify this to work with a C++ function that I added to the Core, but my new Wrapper methods and properties are not showing up in C#. My end-goal is for a C# application send text to a C++ program, the C++ program then query a database, and return the first 20 records that match the text. For now, I just want to send the C++ class a string and an integer, and for it to return a vector of the string repeated integer number of times.

I expected that I would be able to create a new property in the Wrapper, and it would show up in C#. I have the property pointed to a function in Core, and the only significant difference between the working properties/functions and the failing one is the types being used. In the Wrapper project header file, I added my function like this:

void TypeAhead( std::string words, int count );

In the Wrapper .cpp file, I added this:

void Entity::TypeAhead( std::string words, int count )
{
    Console::WriteLine( "The Wrapper is trying to call TypeAhead()!" );
    m_Instance->TypeAhead( words, count );
}

I have matching functions in the Core project. In Program.cs, the Entity class object is able to use the properties and functions from the tutorial, but not the ones I have added. What do I need to change to get properties and functions from the Wrapper project to be usable in the Sandbox project?

My repo can be found here: https://github.com/AdamJHowell/CLIExample

1
  • 1
    There is no interop story for native C++ objects, that's why you are writing a wrapper. All C++ objects, an std::string as well. You don't have to wrap it yourself, the built-in System::String can do it. But you have to add the glue to convert from String^ to std::string, marshal.h makes it a one-liner. Beware that it is a lossy conversion. Commented Mar 17, 2019 at 21:57

2 Answers 2

1

The problem is that std::string is not a valid type when attempting to expose to .NET. It is a pure c++ beast.

Change:

void Entity::TypeAhead( std::string words, int count )
{
    Console::WriteLine( "The Wrapper is trying to call TypeAhead()!" );
    m_Instance->TypeAhead( words, count );
}

...to:

void Entity::TypeAhead( String^ words, int count )
{
    Console::WriteLine( "The Wrapper is trying to call TypeAhead()!" );

    // use your favourite technique to convert to std:string, this 
    // will be a lossy conversion.  Consider using std::wstring.
    std::string converted = // ...
    m_Instance->TypeAhead(converted, count );
}

Use std::wstring internally instead

As indicated by Tom’s fine comments below, you might want to consider using wstring due to possible fidelity loss in the conversion from .NET strings to std::string. To convert see the link below.

Sign up to request clarification or add additional context in comments.

4 Comments

There is another significant issue. std::string can be text using any character encoding. .NET's String uses UTF-16. Any marshaling is going to convert between character encoding. You might need to specify which character encoding to convert to/from. And, if from UTF-16 to anything other than UTF-8, it's going to be lossy. You might need to consider using std::wstring instead.
@TomBlodget whilst true, the essence of the problem is what type to use to expose to .NET. What type the OP uses on the c++ side afterwards is an exercise for the reader. Also, the link provided includes a wstring conversion
Do NOT use Marshal::StringToHGlobalAnsi and Marshal::FreeHGlobal for this. They are not exception safe. (And the names are just atrocious, since they do not work on HGLOBAL handles at all). C++/CLI provides a perfectly good exception-safe conversion named marshal_as which I linked to. This question is not about different string conversion techniques -- it would be a duplicate if it were. In conclusion, the only things present in your answer not already covered in my earlier one are strictly negative.
@MickyD: If you'll actually read the linked question, you'll find that both methods are found on MSDN (yours is listed at the linked question too, and downvoted for good reason). It is really bad to have this "how to convert strings" discussion separately on a hundred different questions because then the bad answers have to be cleaned up a hundred times. Trust the answers on the dedicated questions that specifically compared different conversion techniques, where the experts showed up and voted.
1

That function signature isn't compatible with C#, because it passes a C++ native type by value.

The signature you're looking for is

void TypeAhead( System::String^ words, int count );

and you will need to convert from the .NET String to a C++ std::string before calling the core function.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.