Hi Folks! In this post I will demonstrate how to make c++ and c# communicate with each other using interop and marshalling (by passing string values around).
Giving a little bit of context, c++ will process stuff in the 2-part solution. And c# will receive the updates.
The strings are marshalled into ANSI strings (this is a very important detail about this solution).
If you absolutely need Unicode strings, this solution is not for you.
First, clone the repository https://github.com/guilhermesuzuki/interop.git. You will notice there is a cpp and a csharp project.
TL;DR; recommendation
Well, just run the application, understand how it works and you're good to go! But remember: no unicode strings!
Explanation
The csharp project is a console application developed in .NET 8 and calls the cpp one, a DLL developed in c++.
There are 3 main methods passing strings around them: DoSomethingWithAStringParameter1, DoSomethingWithAStringParameter2, DoSomethingWithAStringParameter3.
Let's dive into the code then! Head to the methods.cpp file inside the cpp project.
The DoSomethingWithAStringParameter1 basically receives a string from the outside world, converts it to the string c++ type and modifies its value, setting the final value back into the parameter in.
There's another crucial function called stringToCharPtr that correctly converts a string back to char*; without it, the example doesn't work.
auto DoSomethingWithAStringParameter1(char* in) -> void
{
std::cout << "in parameter before the change :: " << in << std::endl;
std::string s(in);
s += " -> [Example 1] modified by the method";
in = stringToCharPtr(s);
std::cout << "in parameter after the change :: " << in << std::endl;
}
But what if you need to return a value from the function? Then DoSomethingWithAStringParameter2 and DoSomethingWithAStringParameter3 solve your problems!
The DoSomethingWithAStringParameter2 is a bit different: it also receives a string parameter called in, but it outputs another string from a parameter called out.
auto DoSomethingWithAStringParameter2(char*& out, char* in) -> void
{
std::cout << "in parameter before the change :: " << in << std::endl;
std::string s(in);
s += " -> [Example 2] modified by the method";
out = stringToCharPtr(s);
}
DoSomethingWithAStringParameter2 is useful when you need to return several out parameters.
Finally, let's take a look at DoSomethingWithAStringParameter3. Again it receives an in parameter, converts it to c++ string, appends a text to the value and converts it back to char*, returning the final result.
The difference here lies in the c# side: the correspondent DoSomethingWithAStringParameter3 inside the cppMethods static class, returns an nint (a pointer). So you have to extract the string from that pointer, using the Marshal.PtrToStringAnsi(pointer). So, instead of a string, you gotta deal with a pointer (but everything is in the example application for this post).
auto DoSomethingWithAStringParameter3(char* in) -> char*
{
std::cout << "in parameter before the change :: " << in << std::endl;
std::string s(in);
s += " -> [Example 3] modified by the method";
return stringToCharPtr(s);
}
c#:
internal static class cppMethods
{
const string dllPath = "cpp.dll";
[DllImport(dllPath, EntryPoint = "DoSomethingWithAStringParameter3")]
public static extern nint DoSomethingWithAStringParameter3([MarshalAs(UnmanagedType.LPStr)] string s);
}
Final considerations
The example application from this post doesn't deal with JSON per say, this is your job! But the main concerns are already dealt with in regards to the interop solution. And if you are considering integrating the 2 languages with JSON, have in mind that you have to do 4 string conversions every time a function is called between them:
- one to convert your class object into JSON;
- another conversion inside c++ to convert the string back into the class object;
- another conversion to transform the modified object back into JSON to be returned to the called;
- and a final one to convert the updated string back into the original class object.
If you don't need an updated JSON back from c++, the conversion falls down to 2 times!
I have tried to follow the same logic with unicode strings, but the code won't work (let me know in the comments if you were able to do it).
Hope you guys enjoyed it. See ya!