I have a C++-CLR wrapper around a standard C++ library called from C#. To receive status messages from the library I use a delegate assigned to a callback in the C++ code via Marshal::GetFunctionPointerForDelegate.
This has taken me quite some time to get working and I'm very, very close (I think). The C# delegate is called but the string isn't passed correctly across the boundary.
When I call TakesCallback("Test String") from the C++ code I just get rubbish back in the C# function.
--- The original C++ class and callback function ---
class Solver
{
private:
std::string TakesCallback(const std::string message)
{
return cb(message);
}
public:
// Declare an unmanaged function type that takes a string
typedef std::string (__stdcall* ANSWERCB)(const std::string);
ANSWERCB cb;
};
--- Function to set the callback from the managed wrapper ----
// Set the delegate callback
void ManagedSolver::SetMessageCallback(SendMessageDelegate^ sendMessageDelegate)
{
_sendMessage = sendMessageDelegate;
// Use GetFunctionPointerForDelegate to get the pointer for delegate callback
IntPtr ip = Marshal::GetFunctionPointerForDelegate(sendMessageDelegate);
_solver->cb = static_cast<Solver::ANSWERCB>(ip.ToPointer());
}
--- C# function passed to the C++ \ CLR wrapper SetMessageCallBack ----
private void Message(string message)
{
XtraMessageBox.Show(message, "Done", MessageBoxButtons.OK);
}
To pass a one dimensional string to a function as an argument we just write the name of the string array variable. In the following example we have a string array variable message and it is passed to the displayString function.
To pass a string by value, the string pointer (the s field of the descriptor) is passed. When manipulating IDL strings: Called code should treat the information in the passed IDL_STRING descriptor and the string itself as read-only, and should not modify these values.
Pass by reference Even though C always uses 'pass by value', it is possible simulate passing by reference by using dereferenced pointers as arguments in the function definition, and passing in the 'address of' operator & on the variables when calling the function.
I have been using the code from this page for some months now and I find it very good. It is only one header file you can copy across your projects and it does the job quickly and cleanly.
You use for instance
std::wstring s = clix::marshalString<E_UNICODE>(myCliString);
or
System::String^ s = clix::marshalString<E_ANSI>(mystdstring);
it works both ways and let you specify what encoding you want (ANSI, UTF8 or Windows's unicode -- actually UTF16).
C++ std::string
and .NET System::String
are not interchangeable. C# cannot use the first, and native C++ code cannot use the second. What you need is a C++/CLI function that accepts std::string
and converts it to System::String^
before calling the delegate.
In general, std c++ classes cannot be marshaled to/from C#. If you are building your C++ code as Unicode, I would recommend to change your code into the following:
C++
// Declare an unmanaged function type that takes a string
typedef int (__stdcall* ANSWERCB)(LPCTSTR);
C#
private void Message([MarshalAs(UnmanagedType.LPWStr)]string message)
{
XtraMessageBox.Show(message, "Done", MessageBoxButtons.OK);
}
Take a look at the following example from MSDN
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With