使用托管参数管理非托管回调?
我在非托管C ++中的回调是这样的:
typedef void (*ErrorCallback)(OutputLog& log, std::string& message);
它的用法(代码简化):
class OutputLog
{
private:
ErrorCallback _callback;
public:
void Error(std::string& message)
{
// print message to console/stream here
if (_callback)
{
_callback(*this, message);
}
}
};
在C ++ / CLI中,我为非托管OutputLog类创建了一个包装类。 我定义了这样的回调函数:
public delegate void ErrorCallback(OutputLog^ log, String^ message);
所以我知道我可以通过Marshal::GetFunctionPointerForDelegate
OutputLog^ log
获取函数指针,但是如何将托管参数( OutputLog^ log
和String^ message
)转换为它们的非托管对象( OutputLog& log
和std::string& message
)?
假设你想公开一个托管的OutputLog for .NET客户端来使用它,并将包装好的本地OutputLog传递给一个库,同时允许.NET消费者收到错误通知,你可以使用这些行中的一些东西。
#include "stdafx.h"
#include <string>
#pragma region NATIVE
typedef void (*ErrorCallback)(class OutputLog& log, const std::string& message, void* userData);
class OutputLog
{
private:
ErrorCallback m_callback;
void* m_userData;
public:
OutputLog()
: m_callback(nullptr), m_userData(nullptr) { }
void SetCallback(ErrorCallback callback, void* userData) {
m_callback = callback;
m_userData = userData;
}
void Error(const std::string& message)
{
if (m_callback) {
m_callback(*this, message, m_userData);
}
}
};
#pragma endregion
#pragma region MANAGED
#include <msclr/gcroot.h>
using namespace System;
using namespace System::Runtime::CompilerServices;
class NativeErrorCallbackHandler
{
public:
NativeErrorCallbackHandler(ref class OutputLogManaged^ owner);
private:
static void OnError(class OutputLog& log, const std::string& message, void* userData);
msclr::gcroot<OutputLogManaged^> m_owner;
};
public delegate void ErrorEventHandler(ref class OutputLogManaged^ log, String^ message);
public ref class OutputLogManaged
{
public:
OutputLogManaged()
: m_nativeOutputLog(new OutputLog),
m_nativeHandler(new NativeErrorCallbackHandler(this)) { }
~OutputLogManaged() { // = Dispose
this->!OutputLogManaged();
}
!OutputLogManaged() // = Finalize
{
delete m_nativeOutputLog;
m_nativeOutputLog = nullptr;
delete m_nativeHandler;
m_nativeHandler = nullptr;
}
event ErrorEventHandler^ Error
{
[MethodImplAttribute(MethodImplOptions::Synchronized)]
void add(ErrorEventHandler^ value) {
m_managedHandler = safe_cast<ErrorEventHandler^>(Delegate::Combine(value, m_managedHandler));
}
[MethodImplAttribute(MethodImplOptions::Synchronized)]
void remove(ErrorEventHandler^ value) {
m_managedHandler = safe_cast<ErrorEventHandler^>(Delegate::Remove(value, m_managedHandler));
}
private:
void raise(OutputLogManaged^ log, String^ message) {
auto managedHandler = m_managedHandler;
if (managedHandler != nullptr)
managedHandler(this, message);
}
}
internal:
void RaiseErrorEvent(String^ message) {
Error(this, message);
}
OutputLog* GetNative() { return m_nativeOutputLog; }
private:
OutputLog* m_nativeOutputLog;
NativeErrorCallbackHandler* m_nativeHandler;
ErrorEventHandler^ m_managedHandler;
};
NativeErrorCallbackHandler::NativeErrorCallbackHandler(OutputLogManaged^ owner)
: m_owner(owner)
{
m_owner->GetNative()->SetCallback(&OnError, this);
}
void NativeErrorCallbackHandler::OnError(OutputLog& log, const std::string& message, void* userData)
{
static_cast<NativeErrorCallbackHandler*>(userData)->m_owner->RaiseErrorEvent(
gcnew String(message.c_str(), 0, message.size()));
}
#pragma endregion
#pragma region Test
void Test(OutputLog& log)
{
log.Error("This is a test.");
}
void OnError(OutputLogManaged^ sender, String^ message)
{
Console::WriteLine(message);
}
int main(array<System::String ^> ^args)
{
OutputLogManaged managedLog;
managedLog.Error += gcnew ErrorEventHandler(&OnError);
Test(*managedLog.GetNative());
return 0;
}
#pragma endregion
没有办法“转换” OutputLog^
(如果您打算使用现有的编组函数)。 但是(至少)有一个将String^
转换为std::string
:
#include <msclrmarshal_cppstd.h>
String^ manStr = "BLAH";
std::string stdStr = msclr::interop::marshal_as<std::string>(manStr);
正如在其他答案中提到的,目前还不清楚你想要做什么。 如果您想使用非托管记录器在托管代码中记录错误消息,则可以编写如下所示的代码:
namespace unmanaged
{
class OutputLog;
typedef void(*ErrorCallback)(OutputLog& log, std::string& message);
class OutputLog
{
public:
ErrorCallback _callback;
void Error(std::string& message)
{
if (_callback)
{
_callback(*this, message);
}
}
};
}
namespace managed
{
ref class OutputLog
{
private:
unmanaged::OutputLog *m_log = nullptr;
public:
OutputLog() {}
OutputLog(unmanaged::OutputLog *log)
: m_log(log) {}
void Error(String^ message)
{
// Do something managed stuff, then use the unmanaged logger.
if (m_log != nullptr)
{
std::string stdStrMessage = msclr::interop::marshal_as<std::string>(message);
m_log->Error(stdStrMessage);
}
}
};
}
void PrintMsg(unmanaged::OutputLog& log, std::string& msg)
{
cout << msg << endl;
}
int main(array<System::String ^> ^args)
{
unmanaged::OutputLog *unmanOL = new unmanaged::OutputLog();
unmanOL->_callback = PrintMsg;
managed::OutputLog^ manOL = gcnew managed::OutputLog(unmanOL);
manOL->Error("Hello");
return 0;
}
托管的delegate
被移除并进行managed::OutputLogger
持有对非托管delegate
的引用。