none
Native Callback function wrapping RRS feed

  • Question

  • HI

     

    I have native unamaged library exporting following

     

    typedef void (__stdcall *callback_t)(int handle, void* context, unsigned int notifyEvent);

    status __stdcall setNotifyCallback(int hnd, callback_t callback, void* context, unsigned int notifyFlags);

     

    I have read a lot of forums with different solutions, so now my question is

    What it the best way to wrapp this into .NET C++ CLS managed code??

     

    best reagards, Patrik


    Patrik Nylund
    Wednesday, February 16, 2011 12:04 PM

Answers

  • Untested but should help get you going in the right direction:HTH

    public partial class Form1 : Form
    {
      delegate void NotifyCallbackProc(int handle, IntPtr context, uint notifyEvent);
    
      [DllImport("whatever.dll")] // assumes the dll is in the app's directory or system path
      static extern void setNotifyCallback(int hnd, NotifyCallbackProc callback, IntPtr context, uint notifyFlags);
    
      private readonly NotifyCallbackProc _Callback; // you MUST maintain a reference to the callback to avoid garbage collection
    
      public Form1()
      {
        InitializeComponent();
    
        _Callback = new NotifyCallbackProc(MyCallback);
    
        setNotifyCallback(0, _Callback, IntPtr.Zero, 0);// not sure what your values will be so I put zeroes.
      }
    
      private void MyCallback(int handle, IntPtr context, uint notifyEvent)
      {
        // insert callback code here
      }
    }
    HTH

    ShaneB

    • Proposed as answer by Konrad Neitzel Thursday, February 17, 2011 6:11 AM
    • Marked as answer by patti_nyl Monday, February 21, 2011 7:05 AM
    • Unmarked as answer by patti_nyl Monday, February 21, 2011 7:23 AM
    • Unproposed as answer by patti_nyl Monday, February 21, 2011 7:31 AM
    • Marked as answer by patti_nyl Wednesday, February 23, 2011 1:52 PM
    Thursday, February 17, 2011 5:12 AM
  • If you're in C++ why not use an unmanaged callback?  I've used this pattern a few times:

    (writing this in the forums editor so YMMV on exact syntax)

    class CCallbackContext
    {
    public:
     // managed object instance with method you really want to call...
     gcroot System::Object m_ctxt; 
    
     static void __stdcall callback(int handle, void* context, unsigned int notifyEvent)
     {
       // cast callback paramter back as context and call managed method
       reinterpret_cast<CCallbackContext*>(context)->m_ctxt->SomeMethod(handle, notifyEvent);
     }
    };
    
    

    Then when you want to call the method that needs the callback pointer:

    CCallbackContext ctxt; // unmanaged on stack
    // set the managed object that does the work
    ctxt.m_ctxt = managedThingy;
    // call the unmanaged method that consumes callback
    setNotifyCallback(handle, CCallbackContext::callback, &ctxt, notifyEvent);
    
    

    Hopefully you get the idea ;-)

     

     


    This signature unintentionally left blank.
    • Marked as answer by patti_nyl Wednesday, February 23, 2011 1:52 PM
    Monday, February 21, 2011 11:45 AM
  • On first glance, I'd check for a stack overflow because SetNotifyCallback is calling itself.

    So rather than consuming a method that takes a callback, you're creating a managed wrapper?  I don't believe passing the pointer to the delegate will work because the delegate method expects "this" as the first parameter, however your C++ callback won't know that.

    First, eliminate recursion stack overflow as a possibility.  Then try something like this

    #include < vcclr.h >
    ref class Something
    {
    public:
      delegate void CallbackDelegate(int handle, IntPtr^ context, unsigned int notifyEvent);
    
      static int SetNotifyCallback(int handle, CallbackDelegate^ callback, IntPtr^ context, unsigned int notifyFlags)
      {
        // private native helper to manage callback 
        class CCBData
        {
        public:
          gcroot<Something::CallbackDelegate^> callback;
          gcroot<IntPtr^> context;
    
          // this callback is called by your native method
          static void __stdcall Callback(int handle, void* context, unsigned notifyEvent)
          {
            CCBData* pcbData = reinterpret_cast<CCBData*>(context);
    
            // invoke the delegate with the user supplied context
            pcbData->callback->Invoke(handle, pcbData->context, notifyEvent);
          }
        };
    
        // allocating new here so it stays around after call exits
        // memory leak if not eventually cleaned up. But you can't free it now
        // or you'll get access violation when callback fires.
        CCBData* pcbData = new CCBData();
        pcbData->callback = callback;
        pcbData->context = context;
    
        // call the native method passing native instance of cbData
        // return whatever your status thing is.
        return ::SetNotifyCallback(handle, CCBData::Callback, pcbData, notifyFlags);
      }
    };
    
    

    This signature unintentionally left blank.
    • Marked as answer by patti_nyl Wednesday, February 23, 2011 1:52 PM
    Tuesday, February 22, 2011 11:38 AM

All replies

  • Untested but should help get you going in the right direction:HTH

    public partial class Form1 : Form
    {
      delegate void NotifyCallbackProc(int handle, IntPtr context, uint notifyEvent);
    
      [DllImport("whatever.dll")] // assumes the dll is in the app's directory or system path
      static extern void setNotifyCallback(int hnd, NotifyCallbackProc callback, IntPtr context, uint notifyFlags);
    
      private readonly NotifyCallbackProc _Callback; // you MUST maintain a reference to the callback to avoid garbage collection
    
      public Form1()
      {
        InitializeComponent();
    
        _Callback = new NotifyCallbackProc(MyCallback);
    
        setNotifyCallback(0, _Callback, IntPtr.Zero, 0);// not sure what your values will be so I put zeroes.
      }
    
      private void MyCallback(int handle, IntPtr context, uint notifyEvent)
      {
        // insert callback code here
      }
    }
    HTH

    ShaneB

    • Proposed as answer by Konrad Neitzel Thursday, February 17, 2011 6:11 AM
    • Marked as answer by patti_nyl Monday, February 21, 2011 7:05 AM
    • Unmarked as answer by patti_nyl Monday, February 21, 2011 7:23 AM
    • Unproposed as answer by patti_nyl Monday, February 21, 2011 7:31 AM
    • Marked as answer by patti_nyl Wednesday, February 23, 2011 1:52 PM
    Thursday, February 17, 2011 5:12 AM
  • Hi,

    I think http://www.codeproject.com/KB/dotnet/Cdecl_CSharp_VB.aspx could be helpfull in case you want to find some more description about how to do it. But what I saw, ShaneB got all important parts that are required.

    With kind regards,

    Konrad

    Thursday, February 17, 2011 6:11 AM
  • I guess it is quite the same with Implicit PInvoke?

    Are there any difference between .NET2.0 and .NET4.0 regarding delegates and P/Invoke?

     

    best regards, Patrik


    Patrik Nylund
    Thursday, February 17, 2011 7:41 AM
  • Hi

     

    But how does the code look like in managed C++? Not i C#

     

    with kind regards, Patrik


    Patrik Nylund
    Monday, February 21, 2011 7:24 AM
  • If you're in C++ why not use an unmanaged callback?  I've used this pattern a few times:

    (writing this in the forums editor so YMMV on exact syntax)

    class CCallbackContext
    {
    public:
     // managed object instance with method you really want to call...
     gcroot System::Object m_ctxt; 
    
     static void __stdcall callback(int handle, void* context, unsigned int notifyEvent)
     {
       // cast callback paramter back as context and call managed method
       reinterpret_cast<CCallbackContext*>(context)->m_ctxt->SomeMethod(handle, notifyEvent);
     }
    };
    
    

    Then when you want to call the method that needs the callback pointer:

    CCallbackContext ctxt; // unmanaged on stack
    // set the managed object that does the work
    ctxt.m_ctxt = managedThingy;
    // call the unmanaged method that consumes callback
    setNotifyCallback(handle, CCallbackContext::callback, &ctxt, notifyEvent);
    
    

    Hopefully you get the idea ;-)

     

     


    This signature unintentionally left blank.
    • Marked as answer by patti_nyl Wednesday, February 23, 2011 1:52 PM
    Monday, February 21, 2011 11:45 AM
  • I am doing this because I am making a static CLR-compliance managed interface for a native library, but don't know the managed C++ syntax. :)

     

    But let's see if I understand you example, thanks


    Patrik Nylund
    Monday, February 21, 2011 12:20 PM
  • I have come up with following solution, am I getting in some kind of trouble with the following code? Memory leaks?
    
    Stack missmatch?
    
    // !! h-file
    
     __delegate void CallbackDelegate(int handle, IntPtr context, unsigned int notifyEvent);
    
     static Status SetNotifyCallback(int hnd, CallbackDelegate* callback, IntPtr context, unsigned int notifyFlags);
    
    
    // !! cpp-file
    Status SetNotifyCallback(int hnd, CallbackDelegate* callback, 
    	 IntPtr context, unsigned int notifyFlags)
     {
    	 IntPtr ip = Marshal::GetFunctionPointerForDelegate(callback);
    	 Callback_t cb = static_cast<Callback_t>(ip.ToPointer());
    	 return SetNotifyCallback(hnd, cb, (void*)context, notifyFlags);
     }
    

    Patrik Nylund
    Monday, February 21, 2011 12:29 PM
  • On first glance, I'd check for a stack overflow because SetNotifyCallback is calling itself.

    So rather than consuming a method that takes a callback, you're creating a managed wrapper?  I don't believe passing the pointer to the delegate will work because the delegate method expects "this" as the first parameter, however your C++ callback won't know that.

    First, eliminate recursion stack overflow as a possibility.  Then try something like this

    #include < vcclr.h >
    ref class Something
    {
    public:
      delegate void CallbackDelegate(int handle, IntPtr^ context, unsigned int notifyEvent);
    
      static int SetNotifyCallback(int handle, CallbackDelegate^ callback, IntPtr^ context, unsigned int notifyFlags)
      {
        // private native helper to manage callback 
        class CCBData
        {
        public:
          gcroot<Something::CallbackDelegate^> callback;
          gcroot<IntPtr^> context;
    
          // this callback is called by your native method
          static void __stdcall Callback(int handle, void* context, unsigned notifyEvent)
          {
            CCBData* pcbData = reinterpret_cast<CCBData*>(context);
    
            // invoke the delegate with the user supplied context
            pcbData->callback->Invoke(handle, pcbData->context, notifyEvent);
          }
        };
    
        // allocating new here so it stays around after call exits
        // memory leak if not eventually cleaned up. But you can't free it now
        // or you'll get access violation when callback fires.
        CCBData* pcbData = new CCBData();
        pcbData->callback = callback;
        pcbData->context = context;
    
        // call the native method passing native instance of cbData
        // return whatever your status thing is.
        return ::SetNotifyCallback(handle, CCBData::Callback, pcbData, notifyFlags);
      }
    };
    
    

    This signature unintentionally left blank.
    • Marked as answer by patti_nyl Wednesday, February 23, 2011 1:52 PM
    Tuesday, February 22, 2011 11:38 AM
  • Sorry my error

     

    //My managed function

    SetNotifyCallback(int hnd, CallbackDelegate* callback, IntPtr context, unsigned int notifyFlags)
    {
    	// Calls native function
    	native_namespace::SetNotifyCallback( ... );
    }

    So it wont call itself I was just simplifying my code when copied to the forum. Yes My SetNotifyCallback managed C++ function is just wrapping the native function. 


    Patrik Nylund
    Tuesday, February 22, 2011 11:58 AM
  • No worries, I do that myself most days.  Hopefully the rest of the message is meaningful.

    Requirements not withstanding, you might instead consider exposing .NET events rather than exposing the SetCallBack method.   In that case, you'd call SetNotifyCallback yourself passing 'this' for your context, your own unmanaged method, and probably all the flags.  Then your callback implemention could fire the proper .NET event to anyone registered as a listener.  More work, but I think in the end more elegant than simply copying the existing interface in a layer that .NET likes. 

     


    This signature unintentionally left blank.
    Tuesday, February 22, 2011 3:01 PM