How to pass a string from C ++ - CLI to C # through C ++ - CLI calls 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 the delegate assigned to the callback in C ++ code through Marshal :: GetFunctionPointerForDelegate.

It took me quite a while to work, and I'm very, very close (I think). A C # delegate is called, but the string is not passed correctly across the border.

When I call TakesCallback ("Test String") from C ++ code, I just return the garbage to the C # function.

--- C ++ source 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 callback from 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 shell SetMessageCallBack ----

 private void Message(string message) { XtraMessageBox.Show(message, "Done", MessageBoxButtons.OK); } 
+6
source share
4 answers

C ++ std::string and .NET System::String are not interchangeable. C # cannot use the first, and native C ++ code cannot use the second. You need a C ++ / CLI function that takes std::string and converts it to System::String^ before calling the delegate.

+1
source

I have been using the code from this page for several months and I find it very good. This is just one header file that you can copy on your projects, and it does the job quickly and cleanly.

You use for example

 std::wstring s = clix::marshalString<E_UNICODE>(myCliString); 

or

 System::String^ s = clix::marshalString<E_ANSI>(mystdstring); 

it works in both directions and allows you to specify which encoding you want (ANSI, UTF8 or Windows Unicode - actually UTF16).

+3
source

In general, C ++ std classes cannot be bound to / from C #. If you build your C ++ code as Unicode, I would recommend changing your code to the following:

C ++

 // Declare an unmanaged function type that takes a string typedef int (__stdcall* ANSWERCB)(LPCTSTR); 

FROM#

 private void Message([MarshalAs(UnmanagedType.LPWStr)]string message) { XtraMessageBox.Show(message, "Done", MessageBoxButtons.OK); } 

Take a look at the following example from MSDN

+1
source

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); } 

FROM#

 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); } } 
+1
source

Source: https://habr.com/ru/post/886668/


All Articles