Управляется неуправляемым обратным вызовом с управляемыми параметрами?
Мой обратный вызов в неуправляемом 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
а также String^ message
) своим неуправляемым коллегам (OutputLog& log
а также std::string& message
)?
2 ответа
Предполагая, что вы хотите предоставить управляемый 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
Там нет никакого способа "преобразовать" 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
содержит ссылку на неуправляемую.