【发布时间】:2019-01-18 10:44:34
【问题描述】:
我们正在使用IMMNotificationClient 的实例化来通知我们特定音频设备(Foo sound blaster)发生的变化,但是当尚未在计算机上安装尚未插入的 Foo blaster 时, IMMNotificationClient::OnDeviceStateChanged 函数内部的程序段错误(引发访问冲突)
据我所知,这是导致这种情况发生的原因:
- 插入新的 Foo 冲击波
- Windows 向 IMMNotificationClient 发出设备状态更改通知
- 在
IMMNotificationClient::OnDeviceStateChanged函数完成执行之前,Windows 使 COM 对象无效。
这种失效发生在IMMNotificationClient::OnDeviceStateChanged 函数中看似随机的点。
这里有一些示例代码:
#include <mmdeviceapi.h>
#include <functiondiscoverykeys_devpkey.h>
#include <string>
class FooSoundBlasterNotifier: public IMMNotificationClient {
//private member variables
const char * FOO_BLASTER_NAME = "Foo Blaster";
public:
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pDeviceId, DWORD newState)
{
IMMDeviceEnumerator *pDeviceEnumerator;
IMMDevice *pDevice;
IPropertyStore *pStore;
HRESULT hr = CoInitialize(NULL);
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pDeviceEnumerator);
if(SUCCEEDED(hr))
{
hr = pDeviceEnumerator->GetDevice(pDeviceId, &pDevice);
if(SUCCEEDED(hr))
{
hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
if (SUCCEEDED(hr))
{
PROPVARIANT variant;
PropVariantInit(&variant);
hr = pStore->GetValue(PKEY_Device_FriendlyName, &variant);
if (SUCCEEDED(hr))
{
//Code usually crashes about right here
std::wstring friendlyNameW(variant.pwszVal);
std::string friendlyName(friendlyNameW.begin(), friendlyNameW.end());
if(friendlyName.find(FOO_BLASTER_NAME) != std::string::npos)
{
//Log the information about state change
}
//release
}
//all
}
//COM
}
//Objects
}
return S_OK;
}
//Declare other needed functions
};
如何避免使用无效的 Windows COM 对象?抛开这一点,我如何在不关闭整个程序的情况下成功地从访问冲突中恢复?
编辑
这是代码失败的调用跟踪:
common_strnlen_simd<1,1,unsigned short>(const unsigned short * const string, const unsigned __int64 maximum_count) Line 152
at minkernel\crts\ucrt\src\appcrt\string\strnlen.cpp(152)
common_strnlen<1,unsigned short>(const unsigned short * const string, const unsigned __int64 maximum_count) Line 185
at minkernel\crts\ucrt\src\appcrt\string\strnlen.cpp(185)
wcslen(const wchar_t * string) Line 219
at minkernel\crts\ucrt\src\appcrt\string\strnlen.cpp(219)
[External Code]
FooSoundBlaster::OnDeviceStateChanged(const wchar_t * pwstrDeviceId, unsigned long dwNewState)
[External Code]
【问题讨论】:
-
谁在管理
FooSoundBlasterNotifier上的引用计数? -
可能您错误地管理引用计数等。也基于您的信息,仅不可能说出错误在哪里。 在看似随机的点 - 很难准确定位吗?在哪个运营商处,asm 代码?
-
我的意思是,谁来确保 FooSoundBlaster 上有一个出色的 AddRef?也许您有一个竞争条件,即在 OnDeviceStateChanged 仍在运行时释放最后一个引用。
-
@RbMm 请记住
nullptr是合法的BSTR,但std::wstring的构造函数不允许这样做。 -
@Jack - 如果你高兴,请回答自己。