【问题标题】:Avoiding invalidated pointers while using WinAPI (Windows Core Audio)使用 WinAPI (Windows Core Audio) 时避免无效的指针
【发布时间】:2019-01-18 10:44:34
【问题描述】:

我们正在使用IMMNotificationClient 的实例化来通知我们特定音频设备(Foo sound blaster)发生的变化,但是当尚未在计算机上安装尚未插入的 Foo blaster 时, IMMNotificationClient::OnDeviceStateChanged 函数内部的程序段错误(引发访问冲突)

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

  1. 插入新的 Foo 冲击波
  2. Windows 向 IMMNotificationClient 发出设备状态更改通知
  3. 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 - 如果你高兴,请回答自己。

标签: c++ winapi com


【解决方案1】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多