使用托管参数管理非托管回调?

我在非托管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^ logString^ message )转换为它们的非托管对象( OutputLog& logstd::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的引用。

链接地址: http://www.djcxy.com/p/27809.html

上一篇: Managed to unmanaged callback with managed parameters?

下一篇: Pointer to a pointer C++/CLI wrapper