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

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^ logString^ message)转换为它们的非托管参数(OutputLog& logstd::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