2015-09-03 129 views
0

我在非託管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^ logString^ message)轉換爲自己的非託管同行(OutputLog& logstd::string& message)?

回答

1

假設要公開一個管理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 
+0

完美!謝謝。 –

0

有沒有辦法「轉換」的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持有非託管一個參考。

+0

確定這些字符串將準確地轉換? (字符集/嵌入式零) – Deduplicator

+0

@Deduplicator,我做了它沒有進一步的研究。 –