Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a string from C++-CLI to C# via C++-CLI callbacks and delegates

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);
}
like image 771
Richard Adams Avatar asked Apr 26 '11 00:04

Richard Adams


People also ask

Can you pass a string in C?

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.

How do you pass a string?

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.

Can you pass by reference in C?

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.


3 Answers

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).

like image 90
Alexandre C. Avatar answered Oct 06 '22 00:10

Alexandre C.


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.

like image 43
Ben Voigt Avatar answered Oct 05 '22 23:10

Ben Voigt


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

like image 29
yms Avatar answered Oct 05 '22 22:10

yms