Condividi tramite


Uso di CUnknown

[La funzionalità associata a questa pagina, DirectShow, è una funzionalità legacy. È stato sostituito da MediaPlayer, IMFMediaEnginee Acquisizione audio/video in Media Foundation. Queste funzionalità sono state ottimizzate per Windows 10 e Windows 11. Microsoft consiglia vivamente di usare un nuovo codice MediaPlayer, IMFMediaEngine e Acquisizione audio/video in Media Foundation anziché DirectShow, quando possibile. Microsoft suggerisce che il codice esistente che usa le API legacy venga riscritto per usare le nuove API, se possibile.

DirectShow implementa IUnknown in una classe di base denominata CUnknown. È possibile usare CUnknown per derivare altre classi, sovrascrivendo solo i metodi che cambiano tra i componenti. La maggior parte delle altre classi di base in DirectShow deriva da CUnknown, in modo che il componente possa ereditare direttamente da CUnknown o da un'altra classe di base.

INonDelegatingUnknown

CUnknown implementa INonDelegatingUnknown. Gestisce i conteggi dei riferimenti internamente e nella maggior parte dei casi la classe derivata può ereditare i due metodi di conteggio dei riferimenti senza alcuna modifica. Tenere presente che CUnknown elimina se stesso quando il conteggio dei riferimenti scende a zero. D'altra parte, è necessario eseguire l'override di CUnknown::NonDelegatingQueryInterface, perché il metodo nella classe base restituisce E_NOINTERFACE se riceve un IID diverso da IID_IUnknown. Nella classe derivata testare i ID delle interfacce supportate, come illustrato nell'esempio seguente:

STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
    if (riid == IID_ISomeInterface)
    {
        return GetInterface((ISomeInterface*)this, ppv);
    }
    // Default: Call parent class method. 
    // The CUnknown class must be in the inheritance chain.
    return CParentClass::NonDelegatingQueryInterface(riid, ppv);
}

La funzione di utilità getInterface (vedere funzioni helper COM) imposta il puntatore, incrementa il conteggio dei riferimenti in modo thread-safe e restituisce S_OK. Nel caso predefinito, chiamare il metodo della classe base e restituire il risultato. Se si deriva da un'altra classe di base, chiamare invece il metodo NonDelegatingQueryInterface . In questo modo è possibile supportare tutte le interfacce supportate dalla classe padre.

IUnknown

Come accennato in precedenza, la versione di delega di IUnknown è la stessa per ogni componente, perché non fa altro che richiamare l'istanza corretta della versione non di delega. Per praticità, il file di intestazione Combase.h contiene una macro, DECLARE_IUNKNOWN, che dichiara i tre metodi di delega come metodi inline. Si espande al codice seguente:

STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {      
    return GetOwner()->QueryInterface(riid,ppv);            
};                                                          
STDMETHODIMP_(ULONG) AddRef() {                             
    return GetOwner()->AddRef();                            
};                                                          
STDMETHODIMP_(ULONG) Release() {                            
    return GetOwner()->Release();                           
};

La funzione di utilità CUnknown::GetOwner recupera un puntatore all'interfaccia IUnknown del componente proprietario di questo componente. Per un componente aggregato, il proprietario è il componente esterno. In caso contrario, il componente possiede se stesso. Includere la macro DECLARE_IUNKNOWN nella sezione pubblica della definizione della classe.

Costruttore di classe

Il costruttore della classe deve richiamare il metodo del costruttore per la classe padre, oltre a qualsiasi operazione specifica della classe. L'esempio seguente è un metodo di costruttore tipico:

CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr) 
    : CUnknown(tszName, pUnk, phr)
{ 
    /* Other initializations */ 
};

Il metodo accetta i parametri seguenti, che passa direttamente al metodo del costruttore CUnknown.

  • tszName specifica un nome per il componente.
  • pUnk è un puntatore all'aggregazione IUnknown.
  • pHr è un puntatore a un valore HRESULT, che indica l'esito positivo o negativo del metodo.

Sommario

L'esempio seguente mostra una classe derivata che supporta IUnknown e un'interfaccia ipotetica denominata ISomeInterface:

class CMyComponent : public CUnknown, public ISomeInterface
{
public:

    DECLARE_IUNKNOWN;

    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
    {
        if( riid == IID_ISomeInterface )
        {
            return GetInterface((ISomeInterface*)this, ppv);
        }
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }

    CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr) 
        : CUnknown(tszName, pUnk, phr)
    { 
        /* Other initializations */ 
    };

    // More declarations will be added later.
};

In questo esempio vengono illustrati i punti seguenti:

  • La classe CUnknown implementa l'interfaccia IUnknown. Il nuovo componente eredita da CUnknown e da qualsiasi interfaccia supportata dal componente. Il componente può derivare invece da un'altra classe base che eredita da CUnknown.
  • La macro DECLARE_IUNKNOWN dichiara i metodi di delega IUnknown come metodi inline.
  • La classe CUnknown fornisce l'implementazione per INonDelegatingUnknown.
  • Per supportare un'interfaccia diversa da IUnknown, la classe derivata deve eseguire l'override del metodo NonDelegatingQueryInterface e testare l'IID della nuova interfaccia.
  • Il costruttore della classe richiama il metodo del costruttore per CUnknown.

Il passaggio successivo nella scrittura di un filtro consiste nell'consentire a un'applicazione di creare nuove istanze del componente. Ciò richiede una conoscenza delle DLL e della relativa relazione con le class factory e i metodi del costruttore di classi. Per altre informazioni, vedere Come creare una DLL del filtro DirectShow.

Come implementare IUnknown