Uso di CUnknown

[La funzionalità associata a questa pagina, DirectShow, è una funzionalità legacy. È stata sostituita da MediaPlayer, FMMediaEngine e Audio/Video Capture in Media Foundation. Queste funzionalità sono state ottimizzate per Windows 10 e Windows 11. Microsoft consiglia vivamente che il nuovo codice usi MediaPlayer, FMMediaEngine e Audio/Video Capture 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, sostituendo 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 delle situazioni la classe derivata può ereditare i due metodi di conteggio dei riferimenti senza modifiche. Tenere presente che CUnknown si elimina 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 gli 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 di base e restituire il risultato. Se si deriva da un'altra classe di base, chiamare invece il metodo NonDelegatingQueryInterface . Ciò consente di supportare tutte le interfacce supportate dalla classe padre.

IUnknown

Come accennato in precedenza, la versione delegante di IUnknown è la stessa per ogni componente, perché non richiama più l'istanza corretta della versione nondelegazione. 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 è proprietario. 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 per la 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 di IUnknown.
  • pHr è un puntatore a un valore HRESULT, che indica l'esito positivo o negativo del metodo.

Riepilogo

Nell'esempio seguente viene illustrata 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.
};

Questo esempio illustra 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 potrebbe derivare invece da un'altra classe di base che eredita da CUnknown.
  • La macro DECLARE_IUNKNOWN dichiara i metodi IUnknown delegati 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'abilitare un'applicazione per creare nuove istanze del componente. Ciò richiede una comprensione delle DLL e della relativa relazione ai metodi class factory e costruttori di classi. Per altre informazioni, vedere Come creare una DLL del filtro DirectShow.

Come implementare IUnknown