轮询默认端点上的音频会话有时会在 Win7 上崩溃
Polling for audio sessions on default endpoint sometimes crashes on Win7
我正在开发一个应用程序,该应用程序每 X 毫秒轮询一次默认音频呈现端点上音频会话的状态和峰值电平,并基于此实现一些逻辑。
在 Windows 10 上似乎 运行 没问题,但在 Windows 7 上,当我更改默认播放设备时(例如,当我在 USB 耳机和 PC 扬声器之间切换时)偶尔会崩溃.
发生崩溃的确切行在 运行 秒之间发生变化,但通常是在我访问 IAudioSessionControl 或 IAudioSessionControl2 指针以进行各种 WASAPI 调用时。
我可以从 ProcDump 创建并由 WinDbg 分析的堆栈跟踪中收集到的是,当默认播放设备更改时,表示音频会话的 COM 对象被销毁
(即使我已经获得并且仍然持有指向这些对象接口的指针),然后我的轮询线程在我通过接口指针访问它们的地方随机崩溃。
我想也许我做错了什么,所以这让我想到了 Matthew van Eerde 的
blog
和 sample,他在那里进行相同的查询(以及更多),但针对系统中所有可用的音频端点。
所以我修改了他的示例程序,使其每 5 毫秒执行一次,并且仅针对默认渲染端点执行一次,并且我在 Windows 7.
上遇到了同样的偶尔崩溃
这是示例的精简版,在播放设备之间切换时有时会导致崩溃。
我通常会在设备之间来回切换后约 2 分钟内崩溃。 YMMV.
#include <windows.h>
#include <atlbase.h>
#include <stdio.h>
#include <mmdeviceapi.h>
#include <audiopolicy.h>
#include <endpointvolume.h>
#include <functiondiscoverykeys_devpkey.h>
#define LOG(format, ...) wprintf(format L"\n", __VA_ARGS__)
class CoUninitializeOnExit {
public:
CoUninitializeOnExit() {}
~CoUninitializeOnExit() {
CoUninitialize();
}
};
class PropVariantClearOnExit {
public:
PropVariantClearOnExit(PROPVARIANT *p) : m_p(p) {}
~PropVariantClearOnExit() {
PropVariantClear(m_p);
}
private:
PROPVARIANT *m_p;
};
int _cdecl wmain() {
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
if (FAILED(hr)) {
LOG(L"CoInitialize failed: hr = 0x%08x", hr);
return -__LINE__;
}
CoUninitializeOnExit cuoe;
while (1)
{
Sleep(5);
// get default device
CComPtr<IMMDeviceEnumerator> pMMDeviceEnumerator;
hr = pMMDeviceEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
if (FAILED(hr)) {
LOG(L"CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x", hr);
return -__LINE__;
}
CComPtr<IMMDevice> pMMDevice;
hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pMMDevice);
if (FAILED(hr)) {
LOG(L"IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: hr = 0x%08x", hr);
return -__LINE__;
}
// get the name of the endpoint
CComPtr<IPropertyStore> pPropertyStore;
hr = pMMDevice->OpenPropertyStore(STGM_READ, &pPropertyStore);
if (FAILED(hr)) {
LOG(L"IMMDevice::OpenPropertyStore failed: hr = 0x%08x", hr);
return -__LINE__;
}
PROPVARIANT v; PropVariantInit(&v);
PropVariantClearOnExit pvcoe(&v);
hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &v);
if (FAILED(hr)) {
LOG(L"IPropertyStore::GetValue(PKEY_Device_FriendlyName) failed: hr = 0x%08x", hr);
return -__LINE__;
}
if (VT_LPWSTR != v.vt) {
LOG(L"PKEY_Device_FriendlyName has unexpected vartype %u", v.vt);
return -__LINE__;
}
LOG(L"Selected playback device: %s\n", v.pwszVal);
// get a session enumerator
CComPtr<IAudioSessionManager2> pAudioSessionManager2;
hr = pMMDevice->Activate(
__uuidof(IAudioSessionManager2),
CLSCTX_ALL,
nullptr,
reinterpret_cast<void **>(&pAudioSessionManager2)
);
if (FAILED(hr)) {
LOG(L"IMMDevice::Activate(IAudioSessionManager2) failed: hr = 0x%08x", hr);
return -__LINE__;
}
CComPtr<IAudioSessionEnumerator> pAudioSessionEnumerator;
hr = pAudioSessionManager2->GetSessionEnumerator(&pAudioSessionEnumerator);
if (FAILED(hr)) {
LOG(L"IAudioSessionManager2::GetSessionEnumerator() failed: hr = 0x%08x", hr);
return -__LINE__;
}
// iterate over all the sessions
int count = 0;
hr = pAudioSessionEnumerator->GetCount(&count);
if (FAILED(hr)) {
LOG(L"IAudioSessionEnumerator::GetCount() failed: hr = 0x%08x", hr);
return -__LINE__;
}
for (int session = 0; session < count; session++) {
// get the session control
CComPtr<IAudioSessionControl> pAudioSessionControl;
hr = pAudioSessionEnumerator->GetSession(session, &pAudioSessionControl);
if (FAILED(hr)) {
LOG(L"IAudioSessionEnumerator::GetSession() failed: hr = 0x%08x", hr);
return -__LINE__;
}
AudioSessionState state;
hr = pAudioSessionControl->GetState(&state);
if (FAILED(hr)) {
LOG(L"IAudioSessionControl::GetState() failed: hr = 0x%08x", hr);
return -__LINE__;
}
CComPtr<IAudioSessionControl2> pAudioSessionControl2;
hr = pAudioSessionControl->QueryInterface(IID_PPV_ARGS(&pAudioSessionControl2));
if (FAILED(hr)) {
LOG(L"IAudioSessionControl::QueryInterface(IAudioSessionControl2) failed: hr = 0x%08x", hr);
return -__LINE__;
}
DWORD pid = 0;
hr = pAudioSessionControl2->GetProcessId(&pid);
if (FAILED(hr)) {
LOG(L"IAudioSessionControl2::GetProcessId() failed: hr = 0x%08x", hr);
return -__LINE__;
}
hr = pAudioSessionControl2->IsSystemSoundsSession();
if (FAILED(hr)) {
LOG(L"IAudioSessionControl2::IsSystemSoundsSession() failed: hr = 0x%08x", hr);
return -__LINE__;
}
bool bIsSystemSoundsSession = (S_OK == hr);
// get the current audio peak meter level for this session
CComPtr<IAudioMeterInformation> pAudioMeterInformation;
hr = pAudioSessionControl->QueryInterface(IID_PPV_ARGS(&pAudioMeterInformation));
if (FAILED(hr)) {
LOG(L"IAudioSessionControl::QueryInterface(IAudioMeterInformation) failed: hr = 0x%08x", hr);
return -__LINE__;
}
float peak = 0.0f;
hr = pAudioMeterInformation->GetPeakValue(&peak);
if (FAILED(hr)) {
LOG(L"IAudioMeterInformation::GetPeakValue() failed: hr = 0x%08x", hr);
return -__LINE__;
}
LOG(
L" Session #%d\n"
L" State: %d\n"
L" Peak value: %g\n"
L" Process ID: %u\n"
L" System sounds session: %s",
session,
state,
peak,
pid,
(bIsSystemSoundsSession ? L"yes" : L"no")
);
} // session
} // while
return 0;
}
这是故障转储分析的一个实例:https://pastebin.com/tvRV8ukY。
这是核心音频 API 的 Windows 7 实现的问题,还是我做错了什么?
谢谢
我最终通过电子邮件联系了 Matthew van Eerde,他说它看起来确实像 Windows 7 上的 Core Audio API 中的竞争条件,我最好的选择是提交一份向 Microsoft 提出支持请求。
谢谢你的帮助,马修!
我正在开发一个应用程序,该应用程序每 X 毫秒轮询一次默认音频呈现端点上音频会话的状态和峰值电平,并基于此实现一些逻辑。 在 Windows 10 上似乎 运行 没问题,但在 Windows 7 上,当我更改默认播放设备时(例如,当我在 USB 耳机和 PC 扬声器之间切换时)偶尔会崩溃. 发生崩溃的确切行在 运行 秒之间发生变化,但通常是在我访问 IAudioSessionControl 或 IAudioSessionControl2 指针以进行各种 WASAPI 调用时。 我可以从 ProcDump 创建并由 WinDbg 分析的堆栈跟踪中收集到的是,当默认播放设备更改时,表示音频会话的 COM 对象被销毁 (即使我已经获得并且仍然持有指向这些对象接口的指针),然后我的轮询线程在我通过接口指针访问它们的地方随机崩溃。 我想也许我做错了什么,所以这让我想到了 Matthew van Eerde 的 blog 和 sample,他在那里进行相同的查询(以及更多),但针对系统中所有可用的音频端点。 所以我修改了他的示例程序,使其每 5 毫秒执行一次,并且仅针对默认渲染端点执行一次,并且我在 Windows 7.
上遇到了同样的偶尔崩溃这是示例的精简版,在播放设备之间切换时有时会导致崩溃。 我通常会在设备之间来回切换后约 2 分钟内崩溃。 YMMV.
#include <windows.h>
#include <atlbase.h>
#include <stdio.h>
#include <mmdeviceapi.h>
#include <audiopolicy.h>
#include <endpointvolume.h>
#include <functiondiscoverykeys_devpkey.h>
#define LOG(format, ...) wprintf(format L"\n", __VA_ARGS__)
class CoUninitializeOnExit {
public:
CoUninitializeOnExit() {}
~CoUninitializeOnExit() {
CoUninitialize();
}
};
class PropVariantClearOnExit {
public:
PropVariantClearOnExit(PROPVARIANT *p) : m_p(p) {}
~PropVariantClearOnExit() {
PropVariantClear(m_p);
}
private:
PROPVARIANT *m_p;
};
int _cdecl wmain() {
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
if (FAILED(hr)) {
LOG(L"CoInitialize failed: hr = 0x%08x", hr);
return -__LINE__;
}
CoUninitializeOnExit cuoe;
while (1)
{
Sleep(5);
// get default device
CComPtr<IMMDeviceEnumerator> pMMDeviceEnumerator;
hr = pMMDeviceEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
if (FAILED(hr)) {
LOG(L"CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x", hr);
return -__LINE__;
}
CComPtr<IMMDevice> pMMDevice;
hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pMMDevice);
if (FAILED(hr)) {
LOG(L"IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: hr = 0x%08x", hr);
return -__LINE__;
}
// get the name of the endpoint
CComPtr<IPropertyStore> pPropertyStore;
hr = pMMDevice->OpenPropertyStore(STGM_READ, &pPropertyStore);
if (FAILED(hr)) {
LOG(L"IMMDevice::OpenPropertyStore failed: hr = 0x%08x", hr);
return -__LINE__;
}
PROPVARIANT v; PropVariantInit(&v);
PropVariantClearOnExit pvcoe(&v);
hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &v);
if (FAILED(hr)) {
LOG(L"IPropertyStore::GetValue(PKEY_Device_FriendlyName) failed: hr = 0x%08x", hr);
return -__LINE__;
}
if (VT_LPWSTR != v.vt) {
LOG(L"PKEY_Device_FriendlyName has unexpected vartype %u", v.vt);
return -__LINE__;
}
LOG(L"Selected playback device: %s\n", v.pwszVal);
// get a session enumerator
CComPtr<IAudioSessionManager2> pAudioSessionManager2;
hr = pMMDevice->Activate(
__uuidof(IAudioSessionManager2),
CLSCTX_ALL,
nullptr,
reinterpret_cast<void **>(&pAudioSessionManager2)
);
if (FAILED(hr)) {
LOG(L"IMMDevice::Activate(IAudioSessionManager2) failed: hr = 0x%08x", hr);
return -__LINE__;
}
CComPtr<IAudioSessionEnumerator> pAudioSessionEnumerator;
hr = pAudioSessionManager2->GetSessionEnumerator(&pAudioSessionEnumerator);
if (FAILED(hr)) {
LOG(L"IAudioSessionManager2::GetSessionEnumerator() failed: hr = 0x%08x", hr);
return -__LINE__;
}
// iterate over all the sessions
int count = 0;
hr = pAudioSessionEnumerator->GetCount(&count);
if (FAILED(hr)) {
LOG(L"IAudioSessionEnumerator::GetCount() failed: hr = 0x%08x", hr);
return -__LINE__;
}
for (int session = 0; session < count; session++) {
// get the session control
CComPtr<IAudioSessionControl> pAudioSessionControl;
hr = pAudioSessionEnumerator->GetSession(session, &pAudioSessionControl);
if (FAILED(hr)) {
LOG(L"IAudioSessionEnumerator::GetSession() failed: hr = 0x%08x", hr);
return -__LINE__;
}
AudioSessionState state;
hr = pAudioSessionControl->GetState(&state);
if (FAILED(hr)) {
LOG(L"IAudioSessionControl::GetState() failed: hr = 0x%08x", hr);
return -__LINE__;
}
CComPtr<IAudioSessionControl2> pAudioSessionControl2;
hr = pAudioSessionControl->QueryInterface(IID_PPV_ARGS(&pAudioSessionControl2));
if (FAILED(hr)) {
LOG(L"IAudioSessionControl::QueryInterface(IAudioSessionControl2) failed: hr = 0x%08x", hr);
return -__LINE__;
}
DWORD pid = 0;
hr = pAudioSessionControl2->GetProcessId(&pid);
if (FAILED(hr)) {
LOG(L"IAudioSessionControl2::GetProcessId() failed: hr = 0x%08x", hr);
return -__LINE__;
}
hr = pAudioSessionControl2->IsSystemSoundsSession();
if (FAILED(hr)) {
LOG(L"IAudioSessionControl2::IsSystemSoundsSession() failed: hr = 0x%08x", hr);
return -__LINE__;
}
bool bIsSystemSoundsSession = (S_OK == hr);
// get the current audio peak meter level for this session
CComPtr<IAudioMeterInformation> pAudioMeterInformation;
hr = pAudioSessionControl->QueryInterface(IID_PPV_ARGS(&pAudioMeterInformation));
if (FAILED(hr)) {
LOG(L"IAudioSessionControl::QueryInterface(IAudioMeterInformation) failed: hr = 0x%08x", hr);
return -__LINE__;
}
float peak = 0.0f;
hr = pAudioMeterInformation->GetPeakValue(&peak);
if (FAILED(hr)) {
LOG(L"IAudioMeterInformation::GetPeakValue() failed: hr = 0x%08x", hr);
return -__LINE__;
}
LOG(
L" Session #%d\n"
L" State: %d\n"
L" Peak value: %g\n"
L" Process ID: %u\n"
L" System sounds session: %s",
session,
state,
peak,
pid,
(bIsSystemSoundsSession ? L"yes" : L"no")
);
} // session
} // while
return 0;
}
这是故障转储分析的一个实例:https://pastebin.com/tvRV8ukY。
这是核心音频 API 的 Windows 7 实现的问题,还是我做错了什么?
谢谢
我最终通过电子邮件联系了 Matthew van Eerde,他说它看起来确实像 Windows 7 上的 Core Audio API 中的竞争条件,我最好的选择是提交一份向 Microsoft 提出支持请求。
谢谢你的帮助,马修!