2009-04-09 7 views
6

Sto provando a monitorare nuove sessioni audio tramite l'interfaccia COM di IAudioSessionManager2 di Windows 7 (accoppiato con IAudioSessionNotification). Attualmente, IAudioSessionNotification :: OnSessionCreated() non viene mai chiamato e ho esaurito le idee sul perché.Notifiche di IAudioSessionManager2 non inviate

Codice registrazione personalizzato IAudioSessionNotification:

#define SAFE_RELEASE(comObj) \ 
if(comObj != NULL) \ 
    { (comObj)->Release(); comObj = NULL; } 

BOOL success = false; 

HRESULT res; 
IClassFactory* pFactory; 
IMMDevice* pDevice; 
IMMDeviceEnumerator* pEnumerator; 

SESSION_LISTENER = NULL; 
SESSION = NULL; 

res = CoInitialize(NULL); 

if(res != S_OK && res != S_FALSE) 
    return false; 

res = CoGetClassObject(CLSID_CustomAudioFactory, CLSCTX_ALL, NULL, __uuidof(IClassFactory), (void**)&pFactory); 
if(res != S_OK) goto Exit; 

res = pFactory->CreateInstance(NULL, CLSID_CustomAudioNotifications, (void**)&SESSION_LISTENER); 
if(res != S_OK) goto Exit; 

res = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator); 
if(res != S_OK) goto Exit; 

res = pEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice); 
if(res != S_OK) goto Exit; 

res = pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**)&SESSION); 
if(res != S_OK) goto Exit; 

res = SESSION->RegisterSessionNotification(SESSION_LISTENER); 
if(res != S_OK) goto Exit; 

success = true; 

Exit: 
SAFE_RELEASE(pFactory); 
SAFE_RELEASE(pEnumerator); 
SAFE_RELEASE(pDevice); 
if(!success) 
{ 
    SAFE_RELEASE(SESSION_LISTENER); 
    SAFE_RELEASE(SESSION); 
} 

CustomAudioNotifications dichiarazione:

class CustomAudioNotifications : public IAudioSessionNotification 
{ 
public: 
//Constructors 
CustomAudioNotifications() { InterlockedIncrement(&g_notifyCount); m_listener = NULL; } 
~CustomAudioNotifications() { InterlockedDecrement(&g_notifyCount); SAFE_RELEASE(m_listener); } 

//IUnknown interface 
HRESULT __stdcall QueryInterface(
          REFIID riid , 
          void **ppObj); 
ULONG __stdcall AddRef(); 
ULONG __stdcall Release(); 

//Notification 
HRESULT __stdcall OnSessionCreated(IAudioSessionControl *NewSession); 

private: 
LONG m_nRefCount; 
}; 

OnSessionCreated invia solo un messaggio a una finestra ogni volta che si crea una sessione per il momento; che non succede mai. Nel caso in cui le mie supposizioni siano totalmente prive di base, mi aspetto una notifica ogni volta che un'applicazione che deve ancora riprodurre audio inizia a farlo; quindi avviare VLC con un file video dovrebbe comportare immediatamente un avviso, mentre visitare Pandora tramite un browser Web attiverà anche tale avviso.

Il debug mostra che tutti i valori restituiti sono S_OK.

La mia esperienza di COM è piuttosto limitata, quindi indicando i "WTF" generali? sarebbe anche apprezzato.

+0

CustomAudioNotifications :: QueryInterface chiamato? Restituisce S_OK? – sharptooth

+0

Fuori dalla costruzione dell'oggetto tramite la sua fabbrica; No, QueryInterface non viene chiamato. Almeno, il punto di interruzione non è mai scattato. –

risposta

15

Questo è un TON più lavoro di quello che devi fare.

È sufficiente scrivere una classe che deriva da IAudioSessionNotifications - non è necessario scrivere effettivamente un intero oggetto COM e registrarlo.

È necessario utilizzare anche il ruolo eConsole anziché il ruolo eMultimedia. Non importa (se hai un solo dispositivo audio) ma è più corretto.

Il distruttore per la classe CustomAudioNotification deve essere privato, in questo modo si impedisce la distruzione accidentale. Quindi mi piacerebbe scrivere:

CustomAudioNotification *customNotification = new CustomAudioNotification(); SESSION->RegisterSessionNotification(customNotification);

Sono anche supponendo che si è inizializzata COM prima snippet di codice.

AGGIORNAMENTO: Kevin mi ha inviato la sua richiesta e ci sono un paio di altri problemi con la sua applicazione che sono più fondamentale (sto lavorando per ottenere la documentazione per le API di migliorare per evitare qualsiasi confusione in futuro)

Il primo è che la sua applicazione non ha recuperato l'attuale elenco di sessioni. Questa è una delle cose davvero sottili delle API di enumerazione della sessione. Per evitare che una condizione di competizione possa verificarsi quando arriva una notifica di sessione mentre l'applicazione che utilizza le API di sessione è in fase di avvio, l'API di enumerazione delle sessioni scarta nuove notifiche di sessione finché l'applicazione non ha prima recuperato l'elenco delle sessioni esistenti.

Il modello di utilizzo previsto è:

applicazione attiva una manager2 sessione. Registri delle applicazioni per le notifiche di sessione. L'applicazione recupera l'attuale elenco di sessioni per l'endpoint e memorizza gli oggetti di controllo sessione in un elenco (non dimenticare di aggiungere la sessione).

Quando viene creata una nuova sessione, l'applicazione prende un riferimento all'oggetto di controllo di sessione appena creato e lo inserisce nell'elenco se non è già presente. Si noti che l'oggetto del controllo di sessione passato alla notifica verrà distrutto quando viene restituita la notifica di sessione - se si chiama GetSessionEnumerator a questo punto probabilmente NON si terrà la sessione appena creata (potrebbe, tutto dipende dalla tempistica).

L'applicazione gestisce la durata della sessione in base ai propri criteri: finché l'applicazione ha un riferimento al controllo di sessione, l'oggetto del controllo di sessione sarà valido. Non esiste un meccanismo di scadenza per gli oggetti di controllo della sessione audio.

Inoltre, le API di sessione richiedono l'inizializzazione del MTA. Questo è un peccato, ma poiché creiamo oggetti COM (che implementano IAudioSessionControl) su un thread di lavoro, l'API richiede che l'MTA venga creato prima della ricezione della notifica.

+0

@ Kevin, probabilmente non è possibile ottenere una risposta migliore. Larry è uno dei ragazzi che ha scritto il codice di Windows Audio. :) –

Problemi correlati