2013-03-27 21 views
7

Membri di overflow di Stack Hi,QueryInterface non riesce con E_NOINTERFACE su C#

Sono un principiante della programmazione C#. Sto sviluppando uno streaming di base della fotocamera e continuo a catturare l'applicazione. Una volta che l'utente si ferma, lo visualizzerò su overlay usando il concetto di mixaggio bitmap di VMR9.

Cosa ho fatto?

  • sto facendo uso di C# libreria DirectShow da here
  • Prima vengo tutte le interfacce filtri necessari. Trova il dispositivo di acquisizione allegato. Chiamato Render stream con filtro sorgente e vmr9 per pin PREVIEW. Filtro di origine, sample grabber e renderer nullo per STILL PIN.
  • Ho tre pulsanti di menu -> ferma, mostra sovrapposizione e nascondi overlay.
  • Sto facendo uso del campione di bitmap fornito in quella libreria.
  • Ogni volta che l'utente preme il menu Take Still, l'immagine verrà salvata sul desktop e verrà ridimensionata a una risoluzione ridotta e visualizzata sul video overlay.
  • Mostra Overlay e nasconde le chiamate overlay ShowHideBitmap() che eseguono l'operazione dell'interrogazione dell'interfaccia VMR9BitmapMixer dal filtro vmr9, riempie la struttura VMR9AlphaBitmap e quindi chiama la funzione IVMRMixerBitmap9.SetAlphaBitmap.

Quale problema devo affrontare?

  • Dopo aver preso ancora, se chiamo ShowHideBitmap() opzione di menu attraverso, l'immagine viene aggiornato perfettamente su sovrapposizione.
  • Questa è un'altra opzione che esegue l'aggiornamento automatico dell'overlay non appena viene salvato il fermo immagine. Creo il thread basato su eventi e lo ho fatto per attendere l'evento di aggiornamento creato utilizzando EventWaitHandle. Prima di tornare dalla funzione BufferCB di samplegrabber, ho impostato questo evento di aggiornamento. Che a sua volta procede con thread in attesa. All'interno del filo chiamo la funzione ShowHideBitmap. In questo scenario, ricevo il messaggio di errore come segue.

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)

Ecco il blocco di codice di funzione ShowHideBitmap

//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); 
    } 
} 
} 

Ed ecco il filo di attesa per l'evento di aggiornamento.

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

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

ho provato ad aggiornare questo overlay usando il timer che è stato eseguito a sfondo con intervalli di 100 ms. L'uso del timer funzionava bene, ma per questa operazione, l'uso del timer è di cattiva scelta. Quindi mi sono trasferito con il concetto di threading.

Perché l'interfaccia di ricezione non riesce durante la chiamata da thread e funziona correttamente quando si chiama dalle opzioni di menu? Dovrei prendermi cura di qualche cosa speciale? Ho anche provato un thread parametrico, ma senza fortuna.

Grazie in anticipo per il vostro aiuto.

MODIFICA: se ShowHideBitmap viene chiamato dal thread principale, ogni cosa funziona correttamente. Se ShowHideBitmap viene chiamato dal thread worker, l'oggetto COM crea Exception.Come gestire questa operazione cross-thread?

+0

+1. bella domanda. Commenta come non sono completamente sicuro ... Una sorta di aspettativa: la maggior parte degli oggetti COM dovrebbe essere chiamata da un singolo thread ... la documentazione probabilmente chiamerebbe esplicitamente se è possibile chiamarla da più thread. (Ricordo anche vagamente che potrebbe essere necessario inizializzare COM su altro thread prima di accedere all'oggetto cross thread) ... –

+0

Ciao Alexei, sì hai ragione. La chiamata di oggetti COM da più thread diversi dal thread principale sta creando questo errore. Ho anche provato a inizializzare questa interfaccia vmr9mixerBitmap come thread principale e usando la stessa funzione ShowHideBitmap (invece di creare in questa funzione), ma il risultato è lo stesso. Inizializza COM, vuoi dire che dovrei avere un'interfaccia nel thread principale e usare la stessa interfaccia attraverso il thread? C'è una spiegazione dettagliata di questo oggetto COM a cui si accede attraverso il thread? – Spark

+0

Le interfacce VMR sottostanti possono essere passate tra thread anche violando le regole COM. Nel codice nativo però, non gestito. In C# COM l'interoperabilità non è consapevole di questi trucchi sporchi e suppongo che questo possa essere un problema: quando tenta di passare l'interfaccia tramite il marshalling, non è in grado di farlo, perde le interfacce e non le hai disponibili sul thread di lavoro. –

risposta

3

L'eccezione è ratty, non rara in COM. Ciò che significa in realtà è "Non ho idea di come darti un riferimento all'interfaccia che puoi usare da un thread di lavoro". Che è un tipo comune di contrattempo, questi tipi di componenti COM non sono affatto sicuri per i thread. E impongono che prendendosene cura, eseguano automaticamente il marshalling delle chiamate dal thread di lavoro al thread proprietario. O non permettendoti di usarli da un altro thread perché il marshalling sarebbe inutile, rendendolo troppo lento. VMR rientra in quest'ultima categoria.

Questo è molto diverso NET, ma ha anche un sacco di classi che sono completamente thread-pericoloso. Anche cose di base, nessuna delle classi di raccolta è ad esempio. Ma ti permette comunque di usare queste classi in un thread, lasciandolo a te per renderlo thread-safe. Questo spesso va storto, ovviamente, l'uso di un bloccaggio adeguato è un'abilità.

COM ha sempre avuto il filo per la progettazione. Con la filosofia che il threading è molto difficile da ottenere correttamente, dovrebbe essere curato dalle persone intelligenti. Che funziona in modo fantastico il 95% delle volte. E ti dà un'emicrania importante il resto del tempo. Il tipo di emicrania indotta dal difficile da diagnosticare male perf quando COM si occupa di threading. E l'errore di merda che riporta quando non lo fa.

Bene, non si può fare, è davvero necessario utilizzare quell'interfaccia dallo stesso thread che ha creato l'istanza VMR. Assolutamente no.

+0

Ciao Hans, sono tornato dopo i test del lotto. Come specificato, ora l'applicazione funziona bene per quasi due giorni senza alcun problema. Non ho apportato alcuna modifica al codice, l'ho lasciato come tale e testato il giorno successivo. Ha funzionato bene all'improvviso senza alcun problema.Sto esaminando ulteriormente per ricreare questo problema per saperne di più. Tornerò presto. Grazie per i vostri commenti efficaci e preziosi :-) – Spark

1

Ho avuto errore E_NOINTERFACE durante il tentativo di utilizzare oggetto Listener/Event handler dalla libreria Delphi. Per risolvere il problema con il marshaling & thread diversi ho salvato il dispatcher del thread che assegna l'ascoltatore e quindi lo utilizza per generare eventi.

Interfacce:

[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; } 
} 

Poi implementazione (avviso Dispatcher.CurrentDispatcher memorizzato)

[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; 
     } 
    } 
} 

Poi nel codice:

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

Senza dispacther se Listener viene chiamato in discussione sia diverso produrrà l'errore

Problemi correlati