В настоящее время у меня есть приложение QT, которое аварийно завершает работу, когда я выпускаю последние экземпляры IAudioEndpointVolume или ISimpleAudioVolume , после отключения связанного AudioEndpointDevice (в windows настройках звука) .
Следующая функция вызывается с помощью pOutputDevice->deleteLater()
(из Qt) из обработчика событий, где сообщается об измененном состоянии устройства. Это гарантирует, что окончательные ссылки не освобождаются при выполнении обработчика событий для измененного состояния (как того требует MS do c). Деструктор вызывается из того же потока, что и конструктор, как того требует MS do c.
OutputDevice::~OutputDevice () {
if (pEndpointVolume)
pEndpointVolume->UnregisterControlChangeNotify(systemNotifier); // note, that this executes correctly
if (pEndpointId)
CoTaskMemFree(pEndpointId);
if (pSessManager2)
pSessManager2->UnregisterSessionNotification(systemNotifier);
SafeRelease(&pEndpointVolume); // this command crashes the application
SafeRelease(&pSessManager2);
SafeRelease(&pMMDevice);
SafeRelease(&pPropertyStore);
}
Что мне здесь не хватает? Я мог бы пропустить освобождение отключенных устройств при удалении, но тогда я опасался бы утечки памяти на этом этапе.
Та же ошибка относится к ISimpleAudioVolume
интерфейсам связанных аудио сеансов.
Дополнительная информация о коде
Следующий код создает объект OutputDevice:
OutputDevice::OutputDevice (IMMDevice *pDevice, QObject *parent)
: QObject(parent) {
pMMDevice = pDevice;
pMMDevice->AddRef();
IAudioSessionEnumerator *pSessEnumerator = nullptr;
IAudioSessionControl *pSessControl = nullptr;
int sessionCounter = 0;
// create the notifier object
systemNotifier = new Notifier(this);
connect(systemNotifier, &Notifier::sigDeviceVolumeOrMuteChanged,
this, &OutputDevice::sigVolumeOrMuteChanged,
Qt::BlockingQueuedConnection);
connect(systemNotifier, &Notifier::sigSessionCreated,
this, [=](Notifier *n){
addSession(n->propAudioSessionControl.get());
}, Qt::BlockingQueuedConnection);
// get the endpointID
hr = pMMDevice->GetId(&pEndpointId);
FAILCATCH;
// get the device state
hr = pMMDevice->GetState(&state);
FAILCATCH;
// get the endpoint volume
if (newState == DEVICE_STATE_ACTIVE) {
hr = pMMDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL,
nullptr, reinterpret_cast<void**>(&pEndpointVolume));
if (FAILED(hr)) return;
pEndpointVolume->RegisterControlChangeNotify(systemNotifier);
}
// get the property store
hr = pMMDevice->OpenPropertyStore(STGM_READ, &pPropertyStore);
FAILCATCH;
// update the properties
// (disable prohibits double fault signalling and false updates)
disableSignalling = true;
hr = updateDescriptionLong();
FAILCATCH;
hr = updateDescriptionShort();
FAILCATCH;
hr = updateFormFactor();
FAILCATCH;
// register for new sessions
hr = pMMDevice->Activate(__uuidof(IAudioSessionManager2),
CLSCTX_ALL,
nullptr,
reinterpret_cast<void**>(&pSessManager2));
FAILCATCH;
hr = pSessManager2->RegisterSessionNotification(systemNotifier);
FAILCATCH;
// enumerate all sessions (if any available)
hr = pSessManager2->GetSessionEnumerator(&pSessEnumerator);
if (pSessEnumerator) {
FAILCATCH;
hr = pSessEnumerator->GetCount(&sessionCounter);
FAILCATCH;
for (int i = 0; i < sessionCounter; i++) {
SafeRelease(&pSessControl);
hr = pSessEnumerator->GetSession(i, &pSessControl);
FAILCATCH;
addSession(pSessControl);
}
}
else
hr = S_OK;
done:
disableSignalling = false;
if (!disableSignalling && FAILED(hr))
sigErrored(hr);
else if (!disableSignalling) {
sigEndpointIdChanged(pEndpointId);
sigStatusChanged(&state);
}
SafeRelease(&pSessManager2);
SafeRelease(&pSessEnumerator);
SafeRelease(&pSessControl);
}
Следующий код обрабатывает вызов MMDeviceApi для информирования об измененном состоянии устройства:
HRESULT __stdcall Notifier::OnDeviceStateChanged (LPCWSTR a, DWORD b) {
mutex.lock();
propLPCWSTR.set(a);
propDWORD.set(b);
sigDeviceStateChanged(this);
resetProperties();
mutex.unlock();
return S_OK;
}
Следующий код показывает соединение между объектом уведомителя (вызываемым Windows) и обработчиком модели:
connect(systemNotifier, &Notifier::sigDeviceStateChanged,
this, &AudioController::deviceStateChanged,
Qt::BlockingQueuedConnection);
AudioController::deviceStateChanged
будет проверять только новое состояние и вызывать AudioController::removeDevice
.
Следующий код обрабатывает удаление устройства:
void AudioController::removeDevice (LPCWSTR deviceId) {
OutputDevice *pOutputDevice = nullptr;
// findOutputDevice returns true, when the device was found and fill the pointer pOutputDevice with the reference
if (findOutputDevice(deviceId, &pOutputDevice)) {
outputDevices.removeAll(pOutputDevice);
/* ... */
pOutputDevice->deleteLater();
}
}