简体   繁体   中英

DirectShow cast sampleGrabber to ISampleGrabber

I have a strange error that I cannot wrap my head around. I have a graph created in a separate thread that runs and I'm trying to access the IBaseFilter sampleGrabber outside the thread which worked in a console application but I moved the code to a new project and where I'm trying to cast sampleGrabber to ISampleGrabber the runtime complains with a null reference exception. If I debug sampleGrabber it does have the interface ISampleGrabber however I cannot cast it anymore. Moving the code inside the thread running the graph allows me to cast it but its not ideal for my application.

How can a null reference exception appear by casting what clearly is a sampleGrabber to ISampleGrabber fail?

The problem is that DirectShow filters are early COM classes that implement a subset of COM in part of reference counting, interfaces, monikers, persistence - basically all good things lasted for years - however they ignore apartments completely. DirectShow is multithreaded on its own and it is typical that there is a controlling thread, and there are worker streaming threads aside. DirectShow concepts assume you can easily pass interface pointers between threads and no marshaling is involved, expected and required.

Then came .NET with checking COM wrappers, and DirectShow.NET wrapped interface pointers as if they were fully featured apartment-aware COM pointers. At the same time Microsoft stopped giving updates to DirectShow (such as for example supplying Sample Grabber with free threaded marshaler) and eventually you hit the issue when on .NET you cannot do a supposedly simple thing with the interface pointer.

There is still absolutely no problem working with APIs in native code domain because you can skip marshaling there and work with direct pointers.

You build the graph on one apartment, and then you get a call back from Sample Grabber on another apartment (or, otherwise, in your scenario you just do something on a worker thread). You cannot use original interface pointers, esp. those in member variables, because .NET runtime check would hit apartment mismatch, esp, trying to marshal interface pointer for another apartment.

If it were your custom filter with source code, you could have added a custom IMarshal implementation or leverage free threaded marshaler to fix the .NET issue on native code side, or otherwise add a helper to pass pointers across apartments.

In .NET code domain the best approach would be to avoid working with pointers from multiple apartments. There possibly are choices, but the easiest one I think of off the top of my head is to

  • work in MTA to be able to have multiple threads accessing DirectShow interface pointers
  • use CLSID_FilterGraphNoThread version of Filter Graph Manager
  • initialize and terminate filter graph on a dedicated thread, which during graph operation dispatches window messages

That is, either use STA and no extra threads touching pointers, or otherwise not use STA.

在玩了IGlobalInterface并思考了Roman的评论后,我意识到设置处理samplegrabber的函数最好是在另一个线程中,从而绕过STA。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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