IAudioSessionManager2 уведомления не отправлены - PullRequest
6 голосов
/ 10 апреля 2009

Я пытаюсь отслеживать новые аудио сеансы через COM-интерфейс IAudioSessionManager2 в Windows 7 (в сочетании с IAudioSessionNotification). В настоящее время IAudioSessionNotification :: OnSessionCreated () никогда не вызывается, и у меня закончились идеи о том, почему.

Код регистрации пользовательских 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:

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 просто отправляет сообщение окну всякий раз, когда сеанс создается в настоящее время; чего никогда не бывает. На всякий случай, если мои предположения полностью не соответствуют действительности, я ожидаю уведомления, когда приложение, которое еще не воспроизводит звук, начинает это делать; поэтому запуск VLC с видеофайлом должен немедленно привести к уведомлению, а посещение Pandora через веб-браузер также вызовет такое уведомление.

Отладка показывает, что все возвращаемые значения S_OK.

Мой опыт работы с COM довольно ограничен, поэтому обращаю внимание на общие "WTFs?" также будет оценен.

1 Ответ

15 голосов
/ 11 апреля 2009

Это на тонну больше работы, чем нужно.

Вам просто нужно написать класс, производный от IAudioSessionNotifications - вам не нужно фактически писать целый COM-объект и регистрировать его.

Вы также должны использовать роль eConsole вместо роли eMultimedia. Это не имеет значения (если у вас есть только одно аудиоустройство), но это более правильно.

Деструктор для класса CustomAudioNotification должен быть закрытым - таким образом вы предотвратите случайное уничтожение. Поэтому я бы написал:

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

Я также предполагаю, что вы инициализировали COM перед своим фрагментом кода.

ОБНОВЛЕНО: Кевин прислал мне свое приложение, и есть несколько других проблем с его приложением, которые являются более фундаментальными (я работаю над улучшением документации для API, чтобы избежать путаницы в будущем)

Во-первых, его приложение не получило текущий список сеансов. Это одна из самых тонких вещей в API перечисления сеансов. Чтобы предотвратить состояние гонки, которое может возникнуть при получении уведомления о сеансе, когда приложение, использующее API сеанса, запускается, API перечисления сеанса отбрасывает уведомления о новых сеансах, пока приложение сначала не получит список существующих сеансов.

Ожидаемая схема использования:

Приложение активирует менеджер сессий2. Приложение регистрируется для сеансовых уведомлений. Приложение извлекает текущий список сеансов для конечной точки и сохраняет объекты управления сеансами в список (не забудьте добавить сеанс заново).

Когда создается новый сеанс, приложение берет ссылку на вновь созданный объект управления сеансом и вставляет его в список, если он еще не существует. Обратите внимание, что объект управления сеансом, переданный в уведомление, будет уничтожен при возврате уведомления сеанса - если вы вызовете GetSessionEnumerator в этот момент, он, вероятно, НЕ будет содержать вновь созданный сеанс (возможно, все зависит от времени).

Приложение управляет временем жизни сеанса на основе своих собственных критериев - пока приложение имеет ссылку на управление сеансом, объект управления сеансом будет действительным. Для объектов управления аудиосеансом не существует механизма истечения срока действия.

Кроме того, API сеанса требуют инициализации MTA - это прискорбно, но поскольку мы создаем COM-объекты (которые реализуют IAudioSessionControl) в рабочем потоке, API требует, чтобы MTA был создан до получения уведомления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...