123 lines
3.2 KiB
C++
123 lines
3.2 KiB
C++
#include "DeviceEnumerator.h"
|
|
#include <iostream>
|
|
|
|
std::map<int, Device> DeviceEnumerator::getVideoDevicesMap() {
|
|
return getDevicesMap(CLSID_VideoInputDeviceCategory);
|
|
}
|
|
|
|
std::map<int, Device> DeviceEnumerator::getAudioDevicesMap() {
|
|
return getDevicesMap(CLSID_AudioInputDeviceCategory);
|
|
}
|
|
|
|
// Returns a map of id and devices that can be used
|
|
std::map<int, Device> DeviceEnumerator::getDevicesMap(const GUID deviceClass)
|
|
{
|
|
std::map<int, Device> deviceMap;
|
|
|
|
// TODO add back? Calling from C# seems to not need this
|
|
//HRESULT hr = CoInitialize(nullptr);
|
|
//if (FAILED(hr)) {
|
|
// return deviceMap; // Empty deviceMap as an error
|
|
//}
|
|
|
|
// Create the System Device Enumerator
|
|
ICreateDevEnum *pDevEnum;
|
|
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
|
|
|
|
// If succeeded, create an enumerator for the category
|
|
IEnumMoniker *pEnum = NULL;
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pDevEnum->CreateClassEnumerator(deviceClass, &pEnum, 0);
|
|
if (hr == S_FALSE) {
|
|
hr = VFW_E_NOT_FOUND;
|
|
}
|
|
pDevEnum->Release();
|
|
}
|
|
|
|
// Now we check if the enumerator creation succeeded
|
|
int deviceId = -1;
|
|
if (SUCCEEDED(hr)) {
|
|
// Fill the map with id and friendly device name
|
|
IMoniker *pMoniker = NULL;
|
|
while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
|
|
|
|
IPropertyBag *pPropBag;
|
|
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
|
|
if (FAILED(hr)) {
|
|
pMoniker->Release();
|
|
continue;
|
|
}
|
|
|
|
// Create variant to hold data
|
|
VARIANT var;
|
|
VariantInit(&var);
|
|
|
|
std::string deviceName;
|
|
std::string devicePath;
|
|
|
|
// Read FriendlyName or Description
|
|
hr = pPropBag->Read(L"Description", &var, 0); // Read description
|
|
if (FAILED(hr)) {
|
|
// If description fails, try with the friendly name
|
|
hr = pPropBag->Read(L"FriendlyName", &var, 0);
|
|
}
|
|
// If still fails, continue with next device
|
|
if (FAILED(hr)) {
|
|
VariantClear(&var);
|
|
continue;
|
|
}
|
|
// Convert to string
|
|
else {
|
|
deviceName = ConvertBSTRToMBS(var.bstrVal);
|
|
}
|
|
|
|
VariantClear(&var); // We clean the variable in order to read the next value
|
|
|
|
// We try to read the DevicePath
|
|
hr = pPropBag->Read(L"DevicePath", &var, 0);
|
|
if (FAILED(hr)) {
|
|
VariantClear(&var);
|
|
continue; // If it fails we continue with next device
|
|
}
|
|
else {
|
|
devicePath = ConvertBSTRToMBS(var.bstrVal);
|
|
}
|
|
|
|
// We populate the map
|
|
deviceId++;
|
|
Device currentDevice;
|
|
currentDevice.id = deviceId;
|
|
currentDevice.deviceName = deviceName;
|
|
currentDevice.devicePath = devicePath;
|
|
deviceMap[deviceId] = currentDevice;
|
|
|
|
}
|
|
pEnum->Release();
|
|
}
|
|
CoUninitialize();
|
|
return deviceMap;
|
|
}
|
|
|
|
/*
|
|
This two methods were taken from
|
|
https://stackoverflow.com/questions/6284524/bstr-to-stdstring-stdwstring-and-vice-versa
|
|
*/
|
|
|
|
std::string DeviceEnumerator::ConvertBSTRToMBS(BSTR bstr)
|
|
{
|
|
int wslen = ::SysStringLen(bstr);
|
|
return ConvertWCSToMBS((wchar_t*)bstr, wslen);
|
|
}
|
|
|
|
std::string DeviceEnumerator::ConvertWCSToMBS(const wchar_t* pstr, long wslen)
|
|
{
|
|
int len = ::WideCharToMultiByte(CP_ACP, 0, pstr, wslen, NULL, 0, NULL, NULL);
|
|
|
|
std::string dblstr(len, '\0');
|
|
len = ::WideCharToMultiByte(CP_ACP, 0 /* no flags */,
|
|
pstr, wslen /* not necessary NULL-terminated */,
|
|
&dblstr[0], len,
|
|
NULL, NULL /* no default char */);
|
|
|
|
return dblstr;
|
|
}
|