Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QueryInterface fails with E_NOINTERFACE on C#

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?

like image 854
Spark Avatar asked Mar 27 '13 06:03

Spark


2 Answers

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.

like image 81
Hans Passant Avatar answered Oct 21 '22 06:10

Hans Passant


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

like image 23
Evalds Urtans Avatar answered Oct 21 '22 05:10

Evalds Urtans