2012-07-02 110 views
3

我试图将托管函数指针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与委托ED函数指针导致堆损坏
  • RTSPConnection在本地C++实现和含有C呼叫以及(libavcodec的)
  • RTSPConnection包含两个线程,通信和帧调度线程(调用管理回调)
莫非

有人给我一个面包屑?提前谢谢你。

回答

4

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

using namespace System::Runtime::InteropServices; 

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

嘿梦露,你的意思是WorkerThreadFunc?在我真正的电话中,我在我的frame_callback中有GUI调用,并且我在'Form :: Update()'委托上使用'BeginInvoke'。 – Constantin

+0

@Constantin我通过经验发现,执行BeginInvoke的最佳位置应该在Form实例本身中......它拥有最好的上下文来知道调用线程是否是UI线程。 –

+0

啊,我现在看到你没有在frame_callback中调用任何UI函数;我的错。也许删除__cdecl? –

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

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

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

+0

嗨汉斯,StartStreaming()调用阻塞...由于我使用的库(我讨厌它,但我没有发言权)。我应该在StartStreaming调用之前还包含KeepAlive调用吗? – Constantin

+0

然后,您需要GC :: KeepAlive()调用,以便在StartStreaming正在运行时保持委托对象处于活动状态。 –

+0

太好了,谢谢你Hans! – Constantin

相关问题