My favourite way for passing an array of strings C++-->C# is by using a delegate.
C#:
// If possible use UnmanagedType.LPUTF8Str
// or under Windows rewrite everything to use
// wchar_t, std::wstring and UnmanagedType.LPWStr
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void AddAnsi([MarshalAs(UnmanagedType.LPStr)] string str);
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestReturnArrayStrings(AddAnsi add);
and then
var lst = new List<string>();
TestReturnArrayStrings(lst.Add);
foreach (string str in lst)
{
Console.WriteLine(str);
}
And C++:
#include <string>
#include <vector>
extern "C"
{
__declspec(dllexport) void TestReturnArrayStrings(void (add)(const char* pstr))
{
std::string str1 = "Hello";
std::string str2 = "World";
add(str1.data());
add(str2.data());
// Example with std::vector
add("--separator--"); // You can even use C strings
std::vector<std::string> v = { "Foo", "Bar" };
// for (std::vector<std::string>::iterator it = v.begin(); it != v.end(); ++it)
for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it)
{
add(it->data());
}
add("--separator--"); // You can even use C strings
// With C++ 11
// for (auto& it: v)
for (const auto& it: v)
{
add(it.data());
}
}
}
Here the "trick" is that C# passes to C++ a delegate to the List<string>.Add()
method, and C++ "fills" directly the C# List<>
. The memory managed by C++ remains in the C++ side, the memory managed by the C# remains in the C# side. No problems of cross-memory ownership. As you can imagine, it is quite easy to expand the "trick" to any other .Add()
method, like HashSet<string>
, or Dictionary<string, string>
.
As a sidenote, I've created a github with many examples about marshaling between C/C++ and C# (both .NET Framework and .NET Core/5.0).
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…