c++.netcallbackc++-climixed-mode

CrossContext exception on native to managed boundary


I have developed managed C++ application which return an object of ManagedSubscription to C# clients.
ManagedSubscription receives a callback from C# clients, internally makes its own callback to communicate to native C++ code.
While crossing boundaries from native to managed, I observe a crash.
I have put try-catch in native as well as managed callbacks, but it is not caught.

This is my managed C++ code:

    struct NativeCallbackWrapper
      {
      public:
        typedef std::function<bool(const std::vector<Sample>&, bool)> Callback_t;
        typedef bool(*CallbackPtr)(const std::vector<Sample>&, bool);

        NativeCallbackWrapper(Callback_t callback)
          : m_callback(callback)
        {
        }

        ~NativeCallbackWrapper()
        {
        }

        const Callback_t                                            m_callback;
      };


      //Here is decalred variables in ManagedSubscription.h file
      typedef std::function<bool(const std::vector<DFS::Chart::Misc::Sample>&, bool)> Callback_t;
      typedef bool(*CallbackPtr)(const std::vector<DFS::Chart::Misc::Sample>&, bool);
      delegate bool DelegateFunc(const std::vector<DFS::Chart::Misc::Sample>&, bool);

      NativeCallbackWrapper                        *m_nativeCallbackWrapper;
      System::Action<TradeResponse, bool>^         m_callback;
      DelegateFunc^                                m_delegate;
      System::Runtime::InteropServices::GCHandle   m_delegateHandle;


      //This object is returned to C# application
      ManagedSubscription::ManagedSubscription(
        Action<BidAskResponse, bool>^ callback)
      {
        m_delegate = gcnew DelegateFunc(this, &ManagedSubscription::OnCallback);
        m_delegateHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_delegate);
        m_nativeCallbackWrapper = new NativeCallbackWrapper(static_cast<CallbackPtr>(Marshal::GetFunctionPointerForDelegate(m_delegate).ToPointer()));
        m_callback = callback;
      }

bool ManagedSubscription::OnCallback(const std::vector<Sample>& result, bool disconnected)
  {
    try
    {
        m_callback(samplesData, disconnected);
        return true;
    }
    catch (Exception^ ex)
    {
      return false;
    }
    catch (Object^ ex)
    {
      return false;
    }
  }

  //This is given to natice class which actually calls this callback (ManagedSubscription::OnCallback mentioned above)
  const ManagedSubscription::Callback_t& ManagedSubscription::GetNativeCallback()
  {
    return m_nativeCallbackWrapper->m_callback;
  }

This is my native C++ code:

bool Publish(const std::vector<SampleType>& samples, bool disconnected)
        {
          try
          {
            //This actually calls managed callback (ManagedSubscription::OnCallback)
            return m_subscriptionCallback(samples, disconnected);
          }
          catch (std::exception& e)
          {
            return false;
          }
          catch (...)
          {
            return false;
          }
        }

This is stack trace of crash:

KERNELBASE!RaiseException+68     cdb52105     00007ffb     00000001    
clr!RaiseTheExceptionInternalOnly+33b     cdee43f4     00007ffb     00000000    
clr!RaiseTheException+a4     cdee4460     00007ffb     00000002    
clr!RealCOMPlusThrow+69     cdeb6d72     00007ffb     43d60b70    
clr!Thread::RaiseCrossContextException+333     cdcd24cb     00007ffb     2fbadcf8    
clr!Thread::DoADCallBack+1a8     cdb69535     00007ffb     2fbade01    
clr!UM2MDoADCallBack+b3     cdb681dd     00007ffb     8a988a50    
clr!UMThunkStub+26d     bc090516     00007ffb     2fbaef70    
ManagedChartFeedInterface!DFS::Chart::HistoricalCache::TypedSubscription<DFS::Chart::ChartCache::ForexInstrumentInfo>::Publish+66 

First line of stack trace represents call from native code return m_subscriptionCallback(samples, disconnected); in Publish mentioned above.

Am I doing anything wrong in marshaling of callback?
Is it something related to App-Domains as client app used app-domain concept?


Solution

  • I got quiet helpful answer from other sites on my same problem: I thought to share this here as well. Here is link for that:

    [https://msdn.microsoft.com/en-us/library/367eeye0.aspx][1] says it is not necessary to pin the delegate; the function pointer will remain valid even if the GC relocates the delegate.

    In the CoreCLR sources, it looks like Thread::DoADCallBack calls Thread::RaiseCrossContextException only via the END_DOMAIN_TRANSITION macro, after a different exception has been caught. And Thread::DoADCallback uses that macro only if the application domain does not match.