I have a C# application and a C++ dll library. I want to make them communicate using protobuf. The C# app would be the caller and C++ the sender.
What I’m doing is the following:
caller.cs
public static class CppWrapper
{
private const string InterfaceLibrary = "MyCppDll.dll";
[DllImport(InterfaceLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "CppFunction"), SuppressUnmanagedCodeSecurity]
public static extern bool CppFunction([In] byte[] bytes, [In] int size);
}
MyProtobufMessage proto = new();
// Fill proto message
byte[] bytes = proto.ToByteArray();
bool success = CppWrapper.CppFunction(bytes, bytes.Length);
sender.hpp
extern "C" bool __declspec(dllexport) CppFunction(const void* bin, std::size_t size_bin);
sender.cpp
bool CppFunction(const void* bin, std::size_t size_bin)
{
std::unique_ptr<MyProtobufMessage> proto = std::make_unique<MyProtobufMessage>();
if (!proto->ParseFromArray(buffer, size))
{
return false;
}
// Do things
return true;
}
So far it works well. But what I want now is my C++ function CppFunction
to send another protobuf object as byte array as an output for my C# app to process.
Something in the line of:
extern "C" bool __declspec(dllexport) CppFunction(const void* bin,
std::size_t size_bin,
void* bout, /* protobuf output as byte array */
std::size_t size_bout /* output array size */);
The thing is of course I don’t know the output size in advance and everything. My question is more like, what is the best way to do it? (And as such, how should I also change the C# caller function?).
2
If your question is how to use native data using a pointer and length in C#, you have a few easy options:
- To get a
byte[]
managed array, you can useMarshal.Copy
- To get a
ReadOnlySpan<>
, simply construct it from your pointer and length.
4
You can declare your C++ side like this and use an allocator common to C# and C++, for example here I use the COM allocator (note: today it’s not that important /a/36423272/403671):
void __stdcall CppFunction(void** bout, std::size_t* size_bout)
{
*size_bout = 10000;
*bout = CoTaskMemAlloc(*size_bout); // COM allocator
}
And like this in C# (note a std::size_t
strict equivalent is .NET’s IntPtr
):
[DllImport("mydll")]
private static extern void CppFunction([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out byte[] bout, out IntPtr size);
...
CppFunction(out var bytes, out var size);