简体   繁体   English

在C#中使用C ++回调接口

[英]Using a C++ callback interface in C#

I am writing an application that needs to record video using DirectShow - to do this, I am using the interop library DirectShowLib, which seems to work great. 我正在编写一个需要使用DirectShow录制视频的应用程序-为此,我正在使用Interop库DirectShowLib,它看起来很棒。

However, I now have the need to get a callback notification as samples are written to a file, so I can add data unit extensions. 但是,由于样本已写入文件中,因此现在我需要获取回调通知,因此可以添加数据单元扩展。 According to the msdn documentation, in C++ this is done by implementing the IAMWMBufferPassCallback interface, and passing the resulting object to the SetNotify method of a pin's IAMWMBufferPass interface. 根据msdn文档,在C ++中,这是通过实现IAMWMBufferPassCallback接口并将结果对象传递到引脚的IAMWMBufferPass接口的SetNotify方法来完成的。

So, I created a small class that implements the IAMWMBufferPassCallback interface from DirectShowLib: 因此,我创建了一个小类,该类从DirectShowLib实现IAMWMBufferPassCallback接口:

 class IAMWMBufferPassCallbackImpl : IAMWMBufferPassCallback    
 {  
        private RecordingPlayer player;

        public IAMWMBufferPassCallbackImpl(RecordingPlayer player)
        {
            this.player = player;
        }

        public int Notify(INSSBuffer3 pNSSBuffer3, IPin pPin, long prtStart, long prtEnd)
        {
            if (player.bufferPin == pPin && !player.firstBufferHandled)
            {
                player.firstBufferHandled = true;

                //do stuff with the buffer....
            }

            return 0;
        }

}

I then retrieved the IAMWMBufferPass interface for the required pin, and passed an instance of that class to the SetNotify method: 然后,我检索了所需引脚的IAMWMBufferPass接口,并将该类的实例传递给SetNotify方法:

bufferPassCallbackInterface = new IAMWMBufferPassCallbackImpl(this);

IAMWMBufferPass bPass = (IAMWMBufferPass)DSHelper.GetPin(pWMASFWriter, "Video Input 01");

hr = bPass.SetNotify(bufferPassCallbackInterface);
DsError.ThrowExceptionForHR(hr);  

No exception is thrown, indicating that the SetNotify method succeeded. 没有引发异常,表明SetNotify方法成功。

Now, the problem is, that the Notify method in my callback object never gets called. 现在的问题是,我的回调对象中的Notify方法永远不会被调用。 The video records without a problem, except for the fact that the callback is not getting executed at all. 该视频记录没有问题,只是回调根本没有执行。

Is it a problem with the way I am doing the interop? 我做互操作的方式有问题吗?

I usually work with delegates for function pointers in unsafe C# code. 我通常与不安全的C#代码中的函数指针的委托一起工作。
The only trick is that you need to make sure that the reference to the delegate does not get garbage-collected, as the reference in the unsafe code is not used when counting the references inside the garbage collector. 唯一的技巧是,您需要确保不会对该委托的引用进行垃圾收集,因为在对垃圾收集器中的引用进行计数时,将不使用不安全代码中的引用。 Thus the function either needs to be either static or the object which holds the reference needs an additional artificial reference to ensure its availability. 因此,该功能要么需要是静态的,要么是持有引用的对象需要附加的人工引用以确保其可用性。
This is a tutorial for using delegates on MSDN. 这是在MSDN上使用委托教程

The way you are doing the interop looks totally fine. 您进行互操作的方式看起来很好。 I looked at the MSDN docs and it also looks like you are going the right direction, so I don't know why it wouldn't make the callback. 我看了看MSDN文档,看起来您也朝着正确的方向前进,所以我不知道为什么它不进行回调。

Once you do get it doing the callback, however, you might want to make sure to call Marshal.ReleaseComObject(pNSSBuffer3). 但是,一旦完成回调,您可能需要确保调用Marshal.ReleaseComObject(pNSSBuffer3)。 Sometimes sample buffers aren't released for processing until all other references have been removed. 有时,直到所有其他引用都被删除后,样本缓冲区才会释放出来进行处理。 This is the case with the SampleGrabber, so might be the same for this. SampleGrabber就是这种情况,因此可能与此相同。

You should probably be using a delegate: 您可能应该使用委托:

delegate int NotifyDelegate(INSSBuffer3 pNSSBuffer3, IPin pPin, long prtStart, long prtEnd);

void run(){
  bufferPassCallbackInterface = new IAMWMBufferPassCallbackImpl(this);

  IAMWMBufferPass bPass = (IAMWMBufferPass)DSHelper.GetPin(pWMASFWriter, "Video Input 01");
  NotifyDelegate d = new NotifyDelegate(bufferPassCallbackInterface.Notify);

  hr = bPass.SetNotify(d);
  DsError.ThrowExceptionForHR(hr);  
}

However, I'm not quite sure how your implementing an unmanaged interface on a managed class. 但是,我不太确定如何在托管类上实现非托管接口。 An INSSBuffer3* in C++ does not directly correlate to new INNSBuffer3() in C#. C ++中的INSSBuffer3 *与C#中的新INNSBuffer3()不直接相关。

Edit: 编辑:

In response to your comment, the SetNotify function is defined as 为了回应您的评论,SetNotify函数定义为

HRESULT SetNotify(
  [in]  IAMWMBufferPassCallback *pCallback
);

So, it requires a pointer to an implementation of IAMWMBufferPassCallback. 因此,它需要一个指向IAMWMBufferPassCallback的实现的指针。

MSDN says that pCallback is pCallback a MSDN说pCallback是pCallback a

Pointer to the application's IAMWMBufferPassCallback interface. 指向应用程序的IAMWMBufferPassCallback接口的指针。

The delegate is (an attempt to be) the managed equivalent to a pointer to IAMWMBufferPassCallback. 委托是(试图成为)托管的等效于IAMWMBufferPassCallback的指针。 A call back is, generally, a method, not an object. 回调通常是一种方法,而不是对象。 So, passing in an instance of the delegate should work for you. 因此,传递委托的实例将为您工作。 Another similar example is with InternetSetStatusCallback , where you pass a pointer to an implementation of InternetStatusCallback . 另一个类似的示例是InternetSetStatusCallback ,您可以在其中传递指向InternetStatusCallback的实现的指针。

I would try it, if it doesn't work, why not try a library where someone has already done all the hard work? 我会尝试一下,如果它不起作用,为什么不尝试一个已经有人完成所有辛苦工作的图书馆呢? DirectShowNet may be an option (I've never used it). DirectShowNet可能是一个选项(我从未使用过)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM