Pass Managed Function Pointer As Unmanaged Callbac

2019-02-18 11:42发布

问题:

I am attempting to pass a managed function pointer void (*)(void *) to my unmanaged library. My unmanaged library calls this callback with a pointer to a frame of data protected by a CriticalSection. While the managed callback is running, nothing else can modify the frame of data due to the Critical Section. However, I am getting Access Violations and Heap Corruptions just by entering the callback.

EDIT: I forgot to mention. The StartStreaming() steals the thread it manages. Furthermore, it creates a separate thread for dispatching new data to the given callback. The callback is called in this separate thread.

So far I have done the follow:

//Start Streaming
streaming_thread_ = gcnew Thread(gcnew ThreadStart(&Form1::WorkerThreadFunc));
streaming_thread_->Start();

Where:

extern "C" {
#include "libavcodec\avcodec.h"
#include "libavutil\avutil.h"
}

namespace TEST_OCU {

delegate void myCallbackDelegate(void * usr_data); //Declare a delegate for my unmanaged code

public ref class Form1 : public System::Windows::Forms::Form
{
    public:

    static void WorkerThreadFunc()
    {
        myCallbackDelegate^ del = gcnew myCallbackDelegate(&Form1::frame_callback);

        MessageBox::Show("Starting to Streaming", "Streaming Info");
        if(rtsp_connection_ != NULL)
            rtsp_connection_->StartStreaming();
            //rtsp_connection_->StartStreaming((void (*)(void *)) System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(del).ToPointer() );
        MessageBox::Show("Done Streaming", "Streaming Info");
    }

    static void __cdecl frame_callback(void * frame)
    {
        AVFrame * casted_frame = (AVFrame *)frame;
    }

private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) 
    {
        if(rtsp_connection_ == NULL)
            rtsp_connection_ = new NeyaSystems::RTSPConnection("rtsp://url");
    }

    private: static RTSPConnection * rtsp_connection_ = NULL;
}
}
  • I've omitted a lot of pointless code...
  • StartStreaming defaults to a NULL pointer, in this case I get no corruption
  • StartStreaming with the delegated function pointer causes heap corruption
  • RTSPConnection is implemented in native C++ and contains C calls as well (libavcodec)
  • RTSPConnection contains two threads, communication and frame dispatch thread (calls the managed callback)

Could anyone give me a breadcrumb? Thank you so much in advance.

回答1:

EDIT: not a problem with cross-thread calls. If the unmanaged caller expects to call a __cdecl function, then you have to decorate the delegate type with an UnmanagedFunctionPointerAttribute attribute.

using namespace System::Runtime::InteropServices;

[UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)] 
delegate void myCallbackDelegate(void * usr_data); 


回答2:

 myCallbackDelegate^ del = gcnew myCallbackDelegate(&Form1::frame_callback);

That declares the delegate as a local variable in your method. Local variables are subject to garbage collection right after the last statement that uses them. You are correctly using Marshal::GetFunctionPointerForDelegate() but that's not sufficient to make the garbage collector understand that the delegate is in use, it cannot track references in native code. So the next garbage collection taking place during or after the StartStreaming() call is going to destroy the delegate. And your callback will bomb.

It isn't clear exactly when callbacks stop being made. At the very least you need to put GC::KeepAlive(del); after the StartStreaming() call. If callbacks are made after the WorkerThreadFunc() stops running, likely considering the "Start" in the method call, you must keep the delegate object alive longer by, say, storing it as a field in your form class. Possible declared static to keep it alive until program termination.