如何通过 C++-CLI 回调和委托将字符串从 C++-CLI 传递到 C#

Posted

技术标签:

【中文标题】如何通过 C++-CLI 回调和委托将字符串从 C++-CLI 传递到 C#【英文标题】:How to pass a string from C++-CLI to C# via C++-CLI callbacks and delegates 【发布时间】:2011-04-26 00:03:08 【问题描述】:

我有一个围绕从 C# 调用的标准 C++ 库的 C++-CLR 包装器。为了从库中接收状态消息,我使用通过 Marshal::GetFunctionPointerForDelegate 分配给 C++ 代码中的回调的委托。

我花了很长时间才开始工作,而且我非常非常接近(我认为)。调用了 C# 委托,但字符串未正确跨边界传递。

当我从 C++ 代码中调用 TakesCallback("Test String") 时,我只会在 C# 函数中返回垃圾。

---原始C++类和回调函数---

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

--- 从托管包装器设置回调的函数----

// 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# 函数传递给 C++\CLR 包装器 SetMessageCallBack ----

private void Message(string message)

    XtraMessageBox.Show(message, "Done", MessageBoxButtons.OK);

【问题讨论】:

您确定您的意思是托管 C++? (我认为)Visual Studio 2003 不再支持 C++ 的托管扩展。C++/CLI 是它的替代品,它是一种完全不同的语言...... 嗨,比利,对不起,你当然是对的。我的包装器是围绕标准 C++ 库的 C++/CLR。我已经更新了标题和正文。 好的,我已经解决了,std::string 不能与互操作一起使用,因为这里声明了多种实现***.com/questions/874551/stdstring-in-c,只要 *** 允许我,我就会回来回答这个问题到。 传递 message.c_str() 代替,与默认编组兼容。 谢谢汉斯,这对我有用。 【参考方案1】:

我已经使用the code from this page 几个月了,我觉得它非常好。它只是一个可以跨项目复制的头文件,它可以快速而干净地完成工作。

你用例如

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

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

它可以双向工作,让您指定所需的编码(ANSI、UTF8 或 Windows 的 unicode——实际上是 UTF16)。

【讨论】:

【参考方案2】:

C++ std::string 和 .NET System::String 不可互换。 C# 不能使用第一个,本地 C++ 代码不能使用第二个。您需要的是一个接受std::string 并在调用委托之前将其转换为System::String^ 的C++/CLI 函数。

【讨论】:

【参考方案3】:

一般来说,std c++ 类不能与 C# 进行封送处理。如果您将 C++ 代码构建为 Unicode,我建议您将代码更改为以下内容:

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

看看下面example from MSDN

【讨论】:

【参考方案4】:

C++(非托管)

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


【讨论】:

以上是关于如何通过 C++-CLI 回调和委托将字符串从 C++-CLI 传递到 C#的主要内容,如果未能解决你的问题,请参考以下文章

(C++/CLI) 如何在 C++ CLI 中获取从本机代码到托管代码的回调?

将成员函数用于 C 样式回调时固定委托

将非托管方法作为回调传递给托管 C++/CLI 类

第十七章 委托

如何最小起订量委托回调和 lambda 方法

如何在 VS 2005 中为委托回调实现自动属性