使用 WinAPI 时避免无效指针(Windows Core Audio)

Avoiding invalidated pointers while using WinAPI (Windows Core Audio)

我们正在使用 IMMNotificationClient 的实例化来通知我们特定音频设备(Foo sound blaster)发生的变化,但是当计算机上尚未安装 Foo blaster 时插入后,IMMNotificationClient::OnDeviceStateChanged 函数

内的程序段错误(引发访问冲突)

据我所知,这是导致这种情况发生的原因:

  1. 插入新的 Foo 玩具枪
  2. Windows 向 IMMNotificationClient
  3. 发送设备状态更改通知
  4. 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]

真正的问题不是 IMMDevice 对象失效,而是 variant.pwszVal 为空。像这样的简单检查:

if(variant.pwszVal /* != NULL, there you go Paul! :) */)
{
    friendlyNameW = variant.pwszVal;
}

应该可以解决上面代码中的问题。