We are using an instantiation of IMMNotificationClient
to notify us of changes that happen with a specific audio device (the Foo sound blaster), but when a Foo blaster that has not yet been installed on the computer yet is plugged in, the program seg-faults (throws an Access Violation) inside of the IMMNotificationClient::OnDeviceStateChanged
function
Here's what's causing this to happen as far as I can tell:
IMMNotificationClient::OnDeviceStateChanged
function can finish execution, Windows invalidates the COM object.This invalidation happens at seemingly random points in the IMMNotificationClient::OnDeviceStateChanged
function.
Here's some sample code:
#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
};
How can I avoid using an invalidated Windows COM object? Baring that, how do I successfully recover from an access violation without having to close down the whole program?
EDIT
Here's a call trace of where the code is failing:
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]
The real problem isn't an invalidation of the IMMDevice object, but rather variant.pwszVal
being null. A simple check like this:
if(variant.pwszVal /* != NULL, there you go Paul! :) */)
{
friendlyNameW = variant.pwszVal;
}
should solve the problem in the above code.