将管理功能指针作为非托管回调传递

时间:2012-07-02 17:18:49

标签: c++ .net winapi c++-cli unmanaged

我正在尝试将托管函数指针void (*)(void *)传递给我的非托管库。我的非托管库使用指向受CriticalSection保护的数据帧的指针来调用此回调。托管回调正在运行时,由于临界区,没有其他任何东西可以修改数据帧。但是,我只是通过输入回调来获取访问冲突和堆损坏。

编辑:我忘了提及。 StartStreaming()窃取了它管理的主题。此外,它创建了一个单独的线程,用于将新数据分派给给定的回调。回调在这个单独的线程中调用。

到目前为止,我已完成以下操作:

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

其中:

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;
}
}
  • 我省略了很多无意义的代码......
  • StartStreaming默认为NULL指针,在这种情况下我没有损坏
  • 带有委托函数指针的
  • StartStreaming导致堆损坏
  • RTSPConnection在本机C ++中实现,并包含C调用(libavcodec)
  • RTSPConnection包含两个线程,通信和帧分派线程(调用托管回调)

有人能给我一个面包屑吗?非常感谢你。

2 个答案:

答案 0 :(得分:4)

编辑:跨线程调用不是问题。如果非托管调用者希望调用__cdecl函数,则必须使用UnmanagedFunctionPointerAttribute属性修饰委托类型。

using namespace System::Runtime::InteropServices;

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

答案 1 :(得分:3)

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

将委托声明为方法中的局部变量。在使用它们的最后一个语句之后,局部变量立即进行垃圾收集。您正确使用Marshal :: GetFunctionPointerForDelegate(),但这不足以使垃圾收集器了解委托正在使用中,它无法跟踪本机代码中的引用。因此,在StartStreaming()调用期间或之后发生的下一次垃圾收集将破坏该委托。而你的回调将会爆炸。

目前尚不清楚回调何时停止。至少你需要把GC :: KeepAlive(del);在StartStreaming()调用之后。如果在WorkerThreadFunc()停止运行后进行回调,可能考虑到方法调用中的“Start”,则必须保持委托对象更长时间,比如将其存储为表单类中的字段。可能声明静态以使其保持活动状态直到程序终止。