4

我正在尝试将托管函数指针传递void (*)(void *)给我的非托管库。我的非托管库使用指向受关键部分保护的数据帧的指针调用此回调。当托管回调正在运行时,由于临界区,没有其他东西可以修改数据帧。但是,仅通过输入回调,我就会遇到访问冲突和堆损坏。

编辑:我忘了提。窃取它管理的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包含两个线程,通信和帧调度线程(调用托管回调)

谁能给我一个面包屑?非常感谢你。

4

2 回答 2

4

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

using namespace System::Runtime::InteropServices;

[UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)] 
delegate void myCallbackDelegate(void * usr_data); 
于 2012-07-02T17:27:40.320 回答
3
 myCallbackDelegate^ del = gcnew myCallbackDelegate(&Form1::frame_callback);

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

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

于 2012-07-02T17:54:40.030 回答