使用托管参数管理到非托管回调?
Managed to unmanaged callback with managed parameters?
我在非托管 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 class 创建了一个包装器 class。我这样定义回调函数:
public delegate void ErrorCallback(OutputLog^ log, String^ message);
所以我知道我可以通过 Marshal::GetFunctionPointerForDelegate
获取函数指针,但是如何将托管参数(OutputLog^ log
和 String^ message
)转换为它们的非托管参数(OutputLog& log
和 std::string& message
)?
无法 "convert" OutputLog^
(如果您打算使用现有的编组函数)。但是(至少)有一个可以将 String^
转换为 std::string
:
#include <msclr\marshal_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
持有对非托管的引用。
假设您想要公开托管的 OutputLog 供 .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
我在非托管 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 class 创建了一个包装器 class。我这样定义回调函数:
public delegate void ErrorCallback(OutputLog^ log, String^ message);
所以我知道我可以通过 Marshal::GetFunctionPointerForDelegate
获取函数指针,但是如何将托管参数(OutputLog^ log
和 String^ message
)转换为它们的非托管参数(OutputLog& log
和 std::string& message
)?
无法 "convert" OutputLog^
(如果您打算使用现有的编组函数)。但是(至少)有一个可以将 String^
转换为 std::string
:
#include <msclr\marshal_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
持有对非托管的引用。
假设您想要公开托管的 OutputLog 供 .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