4

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?

Roman R.
  • 68,205
  • 6
  • 94
  • 158
bl4kh4k
  • 1,440
  • 4
  • 20
  • 34
  • What is the relationship between `IBaseFilter` and `ISampleGrabber`? – Karl Anderson Jun 24 '13 at 18:21
  • IBaseFilter sampleGrabber = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_SampleGrabber)); CLSID_SampleGrabber being the classID of the directshow samplegrabber, which interfaces ISampleGrabber. – bl4kh4k Jun 24 '13 at 18:26
  • It's been 1 month since I asked this question, and I'm still stuck... if anyone has any ideas... please share. Thank you. – bl4kh4k Aug 12 '13 at 15:12
  • start a bounty for your question. – Karl Anderson Aug 12 '13 at 15:13
  • 2
    My guess would be that it's an issue around attempt to illegally pass COM pointer between apartments. There is no such issue in C++ because `ISampleGrabber` is just a pointer. With C# it is more complicated. – Roman R. Aug 12 '13 at 17:41
  • I think your right Roman, do you know of a way to access COM between multiple threads in c#? This is what I am currently trying to attempt. – bl4kh4k Aug 14 '13 at 12:35
  • 1
    COM threading is such a long story, it tired me out writing an answer. Long story short, *never* trust a DirectShow filter that has a threading model of "Both". Some programmer somewhere went like "it might work" and never actually tried it. – Hans Passant Aug 18 '13 at 22:31
  • [To support the COM Apartment threading goodness](http://stackoverflow.com/questions/127188/could-you-explain-sta-and-mta/127240#127240) – rene Aug 19 '13 at 13:24
  • `Moving the code inside the thread running the graph allows me to cast it but its not ideal for my application` , why? You can always implement messaging between threads, also if you let DS run in its own thread, there is no harm in it, if you tell us what you want to do exactly, same can be achieved without moving stuff to different thread. – Akash Kava Aug 19 '13 at 15:18
  • #Akash, I could not move it into the graph thread because a different static class that has its own graph might need to accessing the samplegrabber, so I built a function that pulls in the samplegrabber depending on which graph was running. – bl4kh4k Aug 19 '13 at 15:30

2 Answers2

3

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.

Roman R.
  • 68,205
  • 6
  • 94
  • 158
0

After playing around with IGlobalInterface and pondering over Roman's comment, I realized setting up the function that handles samplegrabber is best to be in another thread, thus bypassing STA.

bl4kh4k
  • 1,440
  • 4
  • 20
  • 34
  • I'd like to award Roman the bounty, being my first bounty... I'm not sure how as I don't see an icon to click. – bl4kh4k Aug 19 '13 at 15:36
  • I expanded the comment into the answer, if that helps. Good luck, I am glad you sorted it out. – Roman R. Aug 19 '13 at 16:42