0

Here's how I obtain the PROPVARIANT structure with WASAPI API related functions:

//Pointer for stored audio stream
IAudioClient *iac = NULL;

//Endpoint device selection
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice;
IMMDeviceCollection *pCollection = NULL;
CoInitialize(NULL);
hr = CoCreateInstance(
    CLSID_MMDeviceEnumerator, NULL,
    CLSCTX_ALL, IID_IMMDeviceEnumerator,
    (void**)&pEnumerator);

hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pCollection);

//Create vector of IMMDevices
UINT endpointCount = NULL;
(*pCollection).GetCount(&endpointCount);
std::vector<IMMDevice**> IMMDevicePP;   //IMMDevice seems to contain all endpoint devices, so why have a collection here?
for (UINT i = 0; i < (endpointCount); i++)
{
    IMMDevice* pp = NULL;
    (*pCollection).Item(i, &pp);
    IMMDevicePP.assign(1, &pp);
}
UINT IMMDeviceCount = IMMDevicePP.size();

//Enumerate Properties of IMMDevices
std::vector<IPropertyStore*> IMMDeviceProperties;
for (int k = 0; k < IMMDeviceCount; k++) {
    IPropertyStore* prop = NULL;
    (**IMMDevicePP[k]).OpenPropertyStore(STGM_READ, &prop);
    IMMDeviceProperties.assign(1, prop);
}
UINT PropertyStoreCount = IMMDeviceProperties.size();

//Find name property of device
std::vector<PROPVARIANT*> properties;
for (int i = 0; i < PropertyStoreCount; i++) {
    DWORD propCount = 1;
    HRESULT countResult = (*IMMDeviceProperties[i]).GetCount(&propCount);
    if (countResult == S_OK) { }
    else {
        int x = 5;
    }
    for (int p = 0; p < propCount; p++) {
        PROPERTYKEY key;
        HRESULT keyResult = (*IMMDeviceProperties[i]).GetAt(p, &key);
        HRESULT getAT;
        PROPVARIANT propVari;
        HRESULT propVariResult = (*IMMDeviceProperties[i]).GetValue(key, &propVari);
        propVari.vt = VT_LPWSTR;
        LPWSTR test = propVari.pwszVal;
        //char pwszValTest;
        //strcpy(&pwszValTest, propVari.pwszVal);
        //WCHAR friendlyName = *propVari.pwszVal;
        properties.assign(1, &propVari);
    }
}

All HRESULT's return S_OK.

The resulting PROPVARIANT struct renders correctly at first glance. However, when inspecting further with VS's property watch all of the string type properties return the error reflected in the title of this question. So when I attempt to retrieve the name of my Audio Endpoint Device which is contained the the pwszVal property of my PROPVARIANT struct like so:

LPWSTR test = propVari.pwszVal;

I am unable to retrieve the desired data. I have tried copying the string with various converter methods to no avail. I know this error is on a ton of questions but I can't seem to crack this error.

Here's the doc for PROPVARIANT and its corresponding properties:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa380072(v=vs.85).aspx

In this documentation it states that "PROPVARIANT member vt is set to VT_LPWSTR" VT_LPWSTR is an enum type and corresponds to the value 31. Whereas VT_BLOB corresponds to the value 65. My vt member is being set to VT_BLOB or 65 instead of 31 or VT_LPWSTR. Why is this so? This is contradictory to the value stated in this documentation:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd370812(v=vs.85).aspx

Manually setting the vt member also does not change/fix the string reading error:

propVari.vt = VT_LPWSTR;

The PKEY_Device_FriendlyName is what I'm essentially after. Any help/tips is much appreciated.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
lonious
  • 676
  • 9
  • 25
  • It's impossible to know for sure what's going wrong without seeing more code. Try calling `PropVariantInit` before the call to `GetValue` and see if that makes a difference. – Jonathan Potter Oct 01 '14 at 19:52
  • I think I posted everything that's relevant. PropVariantInit doesn't seem to help. – lonious Oct 02 '14 at 23:21

3 Answers3

2

You are not filling your vectors correctly. You are storing memory addresses of local variables, not the actual items that variables refer to.

And worse, you are using std::vector::assign() to add items. assign() replaces the entire contents of a vector with the specified value. If you have multiple devices in a collection, you will not end up with a vector of multiple devices. You should be using push_back() instead of assign().

You are making those mistakes with all of your vectors.

On a side note, you should use the -> operator instead of using (*). when calling methods of the objects. It will make the code cleaner and easier to read.

Try this instead:

//Endpoint device selection
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pCollection = NULL;

CoInitialize(NULL);
hr = CoCreateInstance(
    CLSID_MMDeviceEnumerator, NULL,
    CLSCTX_ALL, IID_IMMDeviceEnumerator,
    (void**)&pEnumerator);

hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pCollection);
pEnumerator->Release();

//Create vector of IMMDevices
std::vector<IMMDevice*> IMMDevice;
UINT endpointCount = 0;
hr = pCollection->GetCount(&endpointCount);
if (hr == S_OK) {
    IMMDevice.reserve(endpointCount);
    for (UINT i = 0; i < endpointCount; ++i) {
        IMMDevice *pDevice = NULL;
        hr = pCollection->Item(i, &pDevice);
        if (hr == S_OK) {
            IMMDevice.push_back(pDevice);
        }
    }
}
UINT IMMDeviceCount = IMMDevice.size();

pCollection->Release();

//Enumerate Properties of IMMDevices
std::vector<IPropertyStore*> IMMDeviceProperties;
IMMDeviceProperties.reserve(IMMDeviceCount);
for (int k = 0; k < IMMDeviceCount; k++) {
    IPropertyStore* prop = NULL;
    hr = IMMDevice[k]->OpenPropertyStore(STGM_READ, &prop);
    if (hr == S_OK) {
        IMMDeviceProperties.push_back(prop);
    }
}
UINT PropertyStoreCount = IMMDeviceProperties.size();

//Find name property of devices
std::vector<std::wstring> MMDeviceFriendlyNames;
MMDeviceFriendlyNames.reserve(IMMDeviceCount);
for (int i = 0; i < PropertyStoreCount; i++) {
    PROPVARIANT propVari;
    PropVariantInit(&propVari);
    hr = IMMDeviceProperties[i]->GetValue(PKEY_Device_FriendlyName, &propVari);
    if (hr == S_OK) {
        MMDeviceFriendlyNames.push_back(propVari.pwszVal);
        PropVariantClear(&propVari);
    }
}

// use vectors as needed...

for (UINT i = 0; i < PropertyStoreCount; ++i) {
    IMMDeviceProperties[i]->Release();
}
for (UINT i = 0; i < IMMDeviceCount; ++i) {
    IMMDevice[i]->Release();
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
1

The following code, based upon yours but without the obfuscating vectors appears to work fine. In runnin g it I get "FriendlyName: Speakers / HP (IDT High Definition Audio CODEC)" which seems correct here for this laptop.

When working with COM and without some kind of smart pointer, be really careful to release all the pointers. And always check all the results. COM calls can fail for all kinds of reasons.

#define WINVER _WIN32_WINNT_VISTA
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define STRICT
#include <windows.h>
#include <ole2.h>
#include <mmdeviceapi.h>
#include <propsys.h>
#include <propvarutil.h>
#include <stdio.h>
#include <Functiondiscoverykeys_devpkey.h>

#pragma comment(lib, "ole32")
#pragma comment(lib, "propsys")

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);

static HRESULT
DumpDeviceProperties(IMMDevice *pDevice)
{
    IPropertyStore *pStore = NULL;
    HRESULT hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
    if (SUCCEEDED(hr))
    {
        PROPVARIANT prop;
        PropVariantInit(&prop);
        hr = pStore->GetValue(PKEY_Device_FriendlyName, &prop);
        if (SUCCEEDED(hr))
        {
            if (IsPropVariantString(prop))
                wprintf(L"FriendlyName: %s\n", PropVariantToStringWithDefault(prop, L"(missing)"));
            else
                hr = E_UNEXPECTED;
        }
        PropVariantClear(&prop);
        pStore->Release();
    }
    return hr;
}

int
wmain(int argc, WCHAR *argv[])
{
    HRESULT hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
    if (SUCCEEDED(hr))
    {
        IMMDeviceEnumerator *pEnumerator = NULL;
        hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void **>(&pEnumerator));
        if (SUCCEEDED(hr))
        {
            IMMDeviceCollection *pCollection = NULL;
            hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pCollection);
            if (SUCCEEDED(hr))
            {
                UINT cEndpoints = 0;
                hr = pCollection->GetCount(&cEndpoints);
                if (SUCCEEDED(hr))
                {
                    for (UINT n = 0; SUCCEEDED(hr) && n < cEndpoints; ++n)
                    {
                        IMMDevice *pDevice = NULL;
                        hr = pCollection->Item(n, &pDevice);
                        if (SUCCEEDED(hr))
                        {
                            hr = DumpDeviceProperties(pDevice);
                            pDevice->Release();
                        }
                    }
                }
                pCollection->Release();
            }
            pEnumerator->Release();
        }
        CoUninitialize();
    }
    return SUCCEEDED(hr) ? 0 : 1;
}

Compiled using: cl -nologo -MDd -Zi -W3 -Od lsdevices.cpp with MSVC 2013.

patthoyts
  • 32,320
  • 3
  • 62
  • 93
  • This line was what helped the most `hr = pStore->GetValue(PKEY_Device_FriendlyName, &prop);` thanks – lonious Oct 03 '14 at 00:09
0

Code that I made, for people that wonder on this page like I did:

You also need this policyconfig.h file


#include <windows.h>
#include <ole2.h>
#include <ShellAPI.h>
#include <olectl.h>
#include <mmdeviceapi.h>
#include <propsys.h>
#include <propvarutil.h>
#include <stdio.h>
#include <Functiondiscoverykeys_devpkey.h>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include <atlstr.h>
#include <atlcore.h>
#include "Policyconfig.h"
#include "Propidl.h"
#include "Functiondiscoverykeys_devpkey.h"

#pragma comment(lib, "ole32")
#pragma comment(lib, "propsys")
using namespace std;

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);


class DeviceProps {
public:
    string id;
    string name;
    string dll;
    int iconID = 0;
    bool isActive = false;
};

static HRESULT getDeviceProperty(IMMDevice* pDevice, DeviceProps* output)
{
    IPropertyStore* pStore = NULL;
    HRESULT hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
    if (SUCCEEDED(hr))
    {
        PROPVARIANT prop;
        PropVariantInit(&prop);
        hr = pStore->GetValue(PKEY_Device_FriendlyName, &prop);
        if (SUCCEEDED(hr))
        {
            if (IsPropVariantString(prop))
            {
                std::wstring wstr(PropVariantToStringWithDefault(prop, L"missing"));
                std::string str(wstr.begin(), wstr.end());
                output->name = str.c_str();
            }
            else
                hr = E_UNEXPECTED;
        }
        hr = pStore->GetValue(PKEY_DeviceClass_IconPath, &prop);
        if (SUCCEEDED(hr))
        {
            if (IsPropVariantString(prop))
            {
                PCWSTR propValue = PropVariantToStringWithDefault(prop, L"missing,0");
                std::wstring propW(propValue);
                std::string cPropValue(propW.begin(), propW.end());
                vector<string> strings;
                istringstream f(cPropValue);
                string s;
                while (getline(f, s, ',')) {
                    strings.push_back(s);
                }
                string location = strings[0];
                string id = strings[1];
                output->dll = location;
                output->iconID = stoi(id);
            }
            else
                hr = E_UNEXPECTED;
        }
        PropVariantClear(&prop);
        pStore->Release();
    }
    return hr;
}
std::vector<DeviceProps> EnumAudioDevices(EDataFlow deviceType = eRender)
{
    std::vector<DeviceProps> output;

    HRESULT hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
    if (SUCCEEDED(hr))
    {
        IMMDeviceEnumerator* pEnumerator = NULL;
        hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(&pEnumerator));
        if (SUCCEEDED(hr))
        {
            IMMDevice* pActive = NULL;

            pEnumerator->GetDefaultAudioEndpoint(deviceType , eMultimedia, &pActive);
            DeviceProps activeDevice;
            getDeviceProperty(pActive, &activeDevice);
            LPWSTR aid;
            pActive->GetId(&aid);
            std::wstring aid2(aid);
            std::string aidS(aid2.begin(), aid2.end());
            activeDevice.id = aidS;
            //output.push_back(activeDevice);
            pActive->Release();
            IMMDeviceCollection* pCollection = NULL;
            hr = pEnumerator->EnumAudioEndpoints(deviceType , DEVICE_STATE_ACTIVE, &pCollection);
            if (SUCCEEDED(hr))
            {
                UINT cEndpoints = 0;
                hr = pCollection->GetCount(&cEndpoints);
                if (SUCCEEDED(hr))
                {
                    for (UINT n = 0; SUCCEEDED(hr) && n < cEndpoints; ++n)
                    {
                        IMMDevice* pDevice = NULL;
                        hr = pCollection->Item(n, &pDevice);
                        if (SUCCEEDED(hr))
                        {
                            DeviceProps device;

                            hr = getDeviceProperty(pDevice, &device);

                            LPWSTR id;
                            pDevice->GetId(&id);
                            std::wstring id2(id);
                            std::string idS(id2.begin(), id2.end());
                            device.id = idS;

                            if (device.id == activeDevice.id)
                                device.isActive = true;
                            output.push_back(device);
                            pDevice->Release();
                        }
                    }
                }
                pCollection->Release();
            }
            pEnumerator->Release();
        }

        //CoUninitialize();
    }
    return output;
}

static HRESULT setDefaultDevice(string id)
{
    string:wstring devID(id.begin(), id.end());
    IPolicyConfigVista* pPolicyConfig;

    HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient),
        NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID*)&pPolicyConfig);
    if (SUCCEEDED(hr))
    {
        hr = pPolicyConfig->SetDefaultEndpoint(devID.c_str(), eConsole);
        hr = pPolicyConfig->SetDefaultEndpoint(devID.c_str(), eMultimedia);
        hr = pPolicyConfig->SetDefaultEndpoint(devID.c_str(), eCommunications);
        pPolicyConfig->Release();
    }
    return hr;
}
static int switchDefaultDevice(EDataFlow deviceType = eRender)
{
    std::vector<DeviceProps> result = EnumAudioDevices(deviceType);
    if (!result.empty())
    {
        std::string activateID("");
        for (const auto& device : result)
        {
            if (activateID== "x") {
                activateID = device.id;
                break;
            }
            if (device.isActive) activateID= "x";
        }
        if (activateID == "x" || activateID == "") activateID = result[0].id;
        setDefaultDevice(activateID);
        return 1;
    }
    return 0;
}
int wmain(int argc, WCHAR* argv[])
{
    std::vector<DeviceProps> result = EnumAudioDevices(eRender);
    for (const auto& device : result)
    {
        std::cout << (device.isActive ? "ACTIVE:" : "") << "Name: " << device.name << " DLL: " << device.dll << " (#" << device.iconID << ")" << "\n" << device.id << "\n";
    }
    switchDefaultDevice(eRender);
}

Doger
  • 141
  • 1
  • 8