2016-05-27 3 views
4

Я пишу смешанный мост C++/CLI для смешанного режима, чтобы иметь возможность вызвать в мою библиотеку классов .NET из старого приложения на C++.Использовать собственный указатель функции для прослушивания управляемого события/маршалинга

В одном из моих классов в библиотеке .NET вы можете присоединяться к событию всякий раз, когда нужно выводить какое-либо сообщение (на консоль или что-то другое в зависимости от вызывающего приложения).

class NetApi 
{ 
    public event EventHandler<MessageEventArgs> MessageReported; 
} 

Для вызова этого из родного приложения C++, я определил следующий указатель/делегат мост:

typedef void(*MessageHandler)(const char* msg); 
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg); 

Опуская из клея для подключения все (прикрепление к MessageReported, удаление sender из EventHandler, и т.д .. .), вот как я создаю управляемый делегат из встроенного указателя функции:

class NetApiBridge 
{ 
    public: 
     void SetMessageHandler(MessageHandler handler) 
     { 
      wrappedListener = (ManagedMessageHandler^)Marshal::GetDelegateForFunctionPointer((IntPtr)handler, ManagedMessageHandler::typeid);   
     }   

    private: 
     msclr::auto_gcroot<NetApi^ > wrappedApi; 
     msclr::auto_gcroot<ManagedMessageHandler^ > wrappedListener; 

     // In another helper ref class in fact, but here pseudo code to simplify 
     void onMessageReported(Object^ sender, MessageEventArgs^ e) 
     { 
      if (!wrappedListener) { return; } 

      wrappedListenter(e->Message); // Send message to native function pointer 
     } 
} 

И я почти там w курица создание фиктивного C++ тестового кода:

void messageHandler(const char* s) 
{ 
    cout << s; 
} 
void main() 
{ 
    NetApiBridge api = new NetApiBridge(); 
    api->SetMessageHandler(&messageHandler); 

    api->Measure(); 
    delete api; 
} 

Все идет хорошо, события передаются правильно, за исключением .... кроме я получаю PInvokeStackImbalance от Managed Debugging Assistant при выходе из родного обработчика, и я явно не знаю, почему?

Что не так с маршалингом const char* как UnmanagedType::LPStr здесь с GetDelegateForFunctionPointer?

NB: мост C++ скомпилирован в x86, если это важно знать здесь.

ответ

4
typedef void(*MessageHandler)(const char* msg); 
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg); 

Ваше объявление делегата несовместимо с объявлением указателя функции в 32-битном коде. Стандартное соглашение о вызове в собственном коде почти всегда __cdecl. По умолчанию для делегатов: __stdcall. Несколько причудливый выбор, но вдохновленный, потому что предполагается, что interop является полезным для вызова ОС, Windows и COM используют __stdcall.

Несоответствие прямо сейчас заставляет делегат-заглушку вызывать аргументы из стека. Так же и собственный код, поэтому стек становится несбалансированным на 4 байта. MDA поможет вам диагностировать эту распространенную неудачу.

Вам нужно будет помочь и заставить их согласиться. Либо с заявлением делегата:

using namespace System::Runtime::InteropServices; 
    ... 
    [UnmanagedFunctionPointer(CallingConvention::Cdecl)] 
    delegate void ManagedMessageHandler(String^ msg); 

или объявлении указателя функции:

typedef void (__stdcall * MessageHandler)(const char* msg); 
+0

Ты мой герой на сегодняшний день! Большое спасибо :) – CitizenInsane

+0

Я верну комплимент, лучший вопрос, который я прочитал всю неделю. Очень хорошо, вы очень легко помогли вам. –