6

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);
}
4
  • 2
    Are you sure you mean Managed C++? Managed Extensions for C++ stopped being supported in (I think) Visual Studio 2003. C++/CLI is it's replacement, and it's a completely different language... Commented Apr 26, 2011 at 0:11
  • Hi Billy, sorry you're right of course. My wrapper is C++ / CLR around a standard C++ library. I've updated the title and body. Commented Apr 26, 2011 at 0:17
  • OK I've solved it std::string won't work with interop due to the variety of implementations as stated here stackoverflow.com/questions/874551/stdstring-in-c I'll come back and answer this as soon as StackOverflow allows me to. Commented Apr 26, 2011 at 0:41
  • Pass message.c_str() instead, compatible with the default marshaling. Commented Apr 26, 2011 at 1:58

4 Answers 4

3

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

Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

1

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

Comments

1

C++ (Unmanaged)

class DllContext
{
public:
typedef void (__stdcall *LOG_CALLBECK)(const wchar_t* LogString);
DllContext(LOG_CALLBECK WriteLog) // Constructor
{
    this->WriteLog = WriteLog;
    this->WriteLog(L"Creating сontext..."); // UNICODE string!
}

private:
LOG_CALLBECK WriteLog;
}

// Export function
SENDAUDIOCLIENT_API DllContext *CreateContext(DllContext::LOG_CALLBECK WriteLog)
{
    return new DllContext(WriteLog);
}

C#

class MyClass
{
private delegate void WriteLog_Delegate([MarshalAs(UnmanagedType.LPWStr)]string Mess);
private WriteLog_Delegate WriteLog;

[DllImport("My.dll", EntryPoint = "?CreateContext@@YAPAVDllContext@@P6GXPB_W@Z@Z", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateContext(WriteLog_Delegate WriteLogProc);

private IntPtr Context = IntPtr.Zero;

public MyClass()
{
    this.WriteLog = new WriteLog_Delegate(this.WriteLogToConsole);
        this.Context = CreateContext(this.WriteLog);
}

private void WriteLogToConsole(string Mess)
{
    Console.WriteLine("Message from unmanaged code: {0}", Mess);
}
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.