QueryInterface fails with E_NOINTERFACE on C#
Asked Answered
M

2

8

Hi Stack Overflow members,

I'm a newbie to C# programming. I am developing a basic camera streaming and still capture application. Once user takes still, I will be displaying it on overlay using VMR9's bitmap mixing concept.

What I did?

  • I am making use of C# direct show library from here
  • First I get all required filters interfaces. Find the attached capture device. Called Render stream with source filter and vmr9 for PREVIEW pin. Source filter, sample grabber and null renderer for STILL PIN.
  • I am having three menu buttons -> take still, show overlay and hide overlay.
  • I am making use of bitmap mixer sample provided in that library.
  • Each time user presses Take Still menu, image will be saved in desktop and will be re-sized to small resolution and displayed on video overlay.
  • Show Overlay and hide overlay calls ShowHideBitmap() which perform operation of querying VMR9BitmapMixer interface from vmr9 filter, fills VMR9AlphaBitmap structure and then calls IVMRMixerBitmap9.SetAlphaBitmap function.

What issue I face?

  • After taking still, if I call ShowHideBitmap() through menu option, the still image taken is being updated perfectly on overlay.
  • This is another option that performs automatic update of overlay as soon as still image is saved. I create event based thread and made it to wait for update event created using EventWaitHandle. Before returning from samplegrabber BufferCB function, I set this update event. Which in turn proceeds with waiting thread. Inside thread I call ShowHideBitmap function. In this scenario, I receive error message as follows.

Unable to case COM object of type 'DirectShowLib.VideoMixingRenderer9' to interface type 'DirectShowLib.IVMRMixerBitmap9'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{ced175e5-1935-4820-81bd-ff6ad00c9108}' failed due to the following error: No such interface supported (Exception from HRESULT: 0X80040002 (E_NOINTERFACE)

Here is the code block of ShowHideBitmap function

//Declarations
private static IBaseFilter vmr9 = null;
private static IVMRMixerBitmap9 vmr9mixerBitmap = null;
private IVMRWindowlessControl9 vmr9windowlessCtrl = null;

private static void ShowHideBitmap(Boolean bEnable)
{
int hr = 0;
VMR9AlphaBitmap alphaBmp;

if (!bEnable)
{
    if (vmr9mixerBitmap != null)
    {
        // Get current Alpha Bitmap Parameters
        hr = vmr9mixerBitmap.GetAlphaBitmapParameters(out alphaBmp);
        DsError.ThrowExceptionForHR(hr);

        // Disable them
        alphaBmp.dwFlags = VMR9AlphaBitmapFlags.Disable;

        // Update the Alpha Bitmap Parameters
        hr = vmr9mixerBitmap.UpdateAlphaBitmapParameters(ref alphaBmp);
        DsError.ThrowExceptionForHR(hr);

        // Create a surface from our alpha bitmap
        surface.Dispose();

        vmr9mixerBitmap = null;

        //Release this alpha bitmap source.
        if (alphaBitmap != null)
        {
            alphaBitmap.Dispose();
        }
    }
    return;
}
else
{
    try
    {
        alphaBitmap = BitmapGenerator.GenerateAlphaBitmap();

        // Create a surface from our alpha bitmap
        if(surface == null)
            surface = new Surface(device, alphaBitmap, Pool.SystemMemory);

        // Get the unmanaged pointer
        unmanagedSurface = surface.GetObjectByValue(DxMagicNumber);

        if (vmr9mixerBitmap == null)
            vmr9mixerBitmap = (IVMRMixerBitmap9)vmr9;


        // Set Alpha Bitmap Parameters for using a Direct3D surface
        alphaBmp = new VMR9AlphaBitmap();
        alphaBmp.dwFlags = VMR9AlphaBitmapFlags.EntireDDS;
        alphaBmp.pDDS = unmanagedSurface;
        alphaBmp.rDest = GetDestRectangle();
        alphaBmp.fAlpha = 1.0f;

        // Set Alpha Bitmap Parameters
        hr = vmr9mixerBitmap.SetAlphaBitmap(ref alphaBmp);
        DsError.ThrowExceptionForHR(hr);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
}

And here is the thread the waits for update event.

    Thread overlayupdatethreadhandle = new Thread(new ThreadStart(overlayupdatethread));
            overlayupdatethreadhandle.Start();

    private void overlayupdatethread()
    {
        do
        {
            overlayupdateeventhandle.WaitOne();
            ShowHideBitmap(GlobalVar.m_ShowOverlay);
        } while (true);
    }

I have tried updating this overlay using timer which was running at background with interval of 100ms. Using timer was working good, but for this operation, using timer is of bad choice. So i moved with threading concept.

Why is that getting interface failed while calling from thread and works good when calling from menu options? Should I have to take care of any special thing? I have even tried parametrized thread, but no luck.

Thanks in advance for your help.

EDIT: If ShowHideBitmap is called from Main Thread, every thing works fine. If ShowHideBitmap is called from worker thread, COM object creates Exception. How to handle this cross-thread operation?

Mellon answered 27/3, 2013 at 6:56 Comment(3)
+1. nice question. Comment as I'm not completely sure... Sort of expected: Most COM objects are expected to be called from single thread... documentation would likely explicitly call if you can call it from multiple threads. (I also vaguely remember that you may need to initialize COM on other thread before accessing object cross thread)...Extracurricular
Hi Alexei, yes you are right. Calling COM object from multiple thread other than Main thread is creating this error. I even tried initializing this vmr9mixerBitmap interface is Main thread and using the same in ShowHideBitmap function (Instead of creating in this function), but the result is same. Initialize COM, you mean I should get interface in main thread and use the same interface across thread? Is there is detailed explanation of this COM object being accessed across thread?Mellon
The underlying VMR interfaces can be passed between threads even violating COM rules. In native code though, not managed. In C# COM interop it not aware of these dirty tricks and I suppose this can be a problem: when it tries to pass the interface through marshaling, it is just unable to do so, it loses the interfaces and you don't have them available on the worker thread.Testudinal
M
4

The exception is ratty, not uncommon in COM. What it really means is "I have no idea how to give you an interface reference that you can use from a worker thread". Which is a common kind of mishap, these kind of COM components just are not thread-safe at all. And they enforce that by either taking care of it, marshaling the calls from the worker thread to the owner thread automatically. Or by not letting you use them from another thread at all because marshaling would be pointless, making it way too slow. VMR falls in the latter category.

This is very unlike .NET, it also has a lot of classes that are completely thread-unsafe. Basic stuff too, none of the collection classes are for example. But it lets you use these classes in a thread anyway, leaving it up to you to make it thread-safe. This quite often goes wrong of course, using proper locking is a skill.

COM has always been thread-aware by design. With the philosophy that threading is very hard to get right so it should be taken care of by the smart people. Which works fantastically 95% of the time. And gives you a major migraine the rest of the time. The kind of migraine induced by the hard to diagnose poor perf when COM takes care of threading. And the crappy error reporting when it doesn't.

Well, no can do, you really do have to use that interface from the same thread that created the VMR instance. No way around that.

Muffin answered 27/3, 2013 at 13:51 Comment(1)
Hi Hans, I am back after lot testing. As you specified, now the application works good for almost two days with out any issue. I have not did any changes in code, I left as such and tested the next day. It worked well suddenly without any issue. I am further looking into to recreate this issue to learn more about it. Will get back to you soon. Thank you for your effective and valuable comments:-)Mellon
O
2

I had error E_NOINTERFACE when trying to use Listener / Event handler object from Delphi library. To overcome issue with marshaling & different threads I saved dispatcher of thread that assigns listener and then use it to fire events.

Interfaces:

[ComVisible(true)]
[Guid("2FFC2C20-A27B-4D67-AEA3-350223D3655F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDataSystemInterfaceEventListener
{
    void OnIntializeCompleted(int status);
    void OnTerminateCompleted(int status);
    void OnRunCompleted(int status);
}

[ComVisible(true)]
[Guid("B9953413-A8C9-4CE2-9263-B488CA02E7EC")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDataSystemInterface
{
    void Intialize(string config);
    void StartRun(string conditions);
    void StopRun();
    void Terminate();        

    IDataSystemInterfaceEventListener Listener { get; set; }
}

Then implementation (notice Dispatcher.CurrentDispatcher stored)

[ComVisible(true)]
[Guid("0818F830-DA37-4167-BF31-3A2C55A9BF2B")]        
public class DataSystemModule : IDataSystemInterface
{
    private Dispatcher m_dispatcherListener = null;
    private IDataSystemInterfaceEventListener m_listener = null;
    public IDataSystemInterfaceEventListener Listener
    {
        get
        {
            return m_listener;
        }
        set
        {
            m_dispatcherListener = Dispatcher.CurrentDispatcher;
            m_listener = value;
        }
    } 
}

Then in code:

if (Listener != null)
{
    m_dispatcherListener.Invoke((Action)delegate()
    {
        Listener.OnTerminateCompleted((int)TerminateStatus.Completed);
    });
}

Without dispacther if Listener is called in different thread it will produce error

Ode answered 8/1, 2014 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.