Device WmiQuery::ExtractDeviceDetails()

in library/src/WmiQuery.cpp [195:351]


Device WmiQuery::ExtractDeviceDetails(const com_ptr<IWbemClassObject>& device) const
{
	Device details;
	DeviceDiscoveryError error;
	
	// Retrieve the unique PnP device ID of the device
	unique_variant vtDeviceID;
	error = CheckHresult(device->Get(L"DeviceID", 0, &vtDeviceID, nullptr, nullptr));
	if (error) {
		throw error.Wrap(L"failed to retrieve DeviceID property of PnP device");
	}
	details.ID = winrt::to_hstring(vtDeviceID.bstrVal);
	
	// Retrieve the human-readable description of the device
	unique_variant vtDescription;
	error = CheckHresult(device->Get(L"Description", 0, &vtDescription, nullptr, nullptr));
	if (error) {
		throw error.Wrap(L"failed to retrieve Description property of PnP device");
	}
	details.Description = winrt::to_hstring(vtDescription.bstrVal);
	
	// Retrieve the vendor of the device
	unique_variant vtVendor;
	error = CheckHresult(device->Get(L"Manufacturer", 0, &vtVendor, nullptr, nullptr));
	if (error) {
		throw error.Wrap(L"failed to retrieve Manufacturer property of PnP device");
	}
	details.Vendor = winrt::to_hstring(vtVendor.bstrVal);
	
	// Retrieve the object path for the instance so we can call instance methods with it
	unique_variant vtPath;
	error = CheckHresult(device->Get(L"__Path", 0, &vtPath, nullptr, nullptr));
	if (error) {
		throw error.Wrap(L"failed to retrieve __Path property of PnP device");
	}
	
	// Create an instance of the input parameters type for the `GetDeviceProperties` instance method
	com_ptr<IWbemClassObject> inputArgs;
	error = CheckHresult(this->inputParameters->SpawnInstance(0, inputArgs.put()));
	if (error) {
		throw error.Wrap(L"failed to spawn input parameters instance for Win32_PnPEntity::GetDeviceProperties");
	}
	
	// Populate the input parameters with the list of decive property keys we want to retrieve
	unique_variant vtPropertyKeys = SafeArrayFactory::CreateStringArray({
		L"DEVPKEY_Device_Driver",
		L"DEVPKEY_Device_LocationPaths",
		this->devPropKeyLUID
	});
	error = CheckHresult(inputArgs->Put(L"devicePropertyKeys", 0, &vtPropertyKeys, CIM_FLAG_ARRAY | CIM_STRING));
	if (error) {
		throw error.Wrap(L"failed to assign input parameters array for Win32_PnPEntity::GetDeviceProperties");
	}
	
	// Call the `GetDeviceProperties` instance method
	com_ptr<IWbemCallResult> callResult;
	error = CheckHresult(this->wbemServices->ExecMethod(
		vtPath.bstrVal,
		wil::make_bstr(L"GetDeviceProperties").get(),
		0,
		nullptr,
		inputArgs.get(),
		nullptr,
		callResult.put()
	));
	if (error) {
		throw error.Wrap(L"failed to invoke Win32_PnPEntity::GetDeviceProperties()");
	}
	
	// Retrieve the return value
	com_ptr<IWbemClassObject> returnValue;
	error = CheckHresult(callResult->GetResultObject(WBEM_INFINITE, returnValue.put()));
	if (error) {
		throw error.Wrap(L"failed to retrieve return value for Win32_PnPEntity::GetDeviceProperties");
	}
	
	// Extract the device properties array and verify that it matches the expected type
	unique_variant vtPropertiesArray;
	error = CheckHresult(returnValue->Get(L"deviceProperties", 0, &vtPropertiesArray, nullptr, nullptr));
	if (error) {
		throw error.Wrap(L"failed to retrieve deviceProperties property of Win32_PnPEntity::GetDeviceProperties return value");
	}
	if (vtPropertiesArray.vt != (VT_ARRAY | VT_UNKNOWN)) {
		throw CreateError(L"deviceProperties value was not an array of IUnknown objects");
	}
	
	// Iterate over the device properties array
	SafeArrayIterator<IUnknown*> propertiesIterator(vtPropertiesArray.parray);
	for (auto element : propertiesIterator)
	{
		// Cast the property object to an IWbemClassObject
		IWbemClassObject* object = nullptr;
		error = CheckHresult(element->QueryInterface(&object));
		if (error) {
			throw error.Wrap(L"IUnknown::QueryInterface() failed for Win32_PnPDeviceProperty object");
		}
		
		// Retrieve the key name of the property
		unique_variant vtKeyName;
		error = CheckHresult(object->Get(L"KeyName", 0, &vtKeyName, nullptr, nullptr));
		if (error) {
			throw error.Wrap(L"failed to retrieve KeyName property of PnP device property");
		}
		wstring keyName(winrt::to_hstring(vtKeyName.bstrVal));
		
		// Attempt to retrieve the value of the property
		unique_variant data;
		HRESULT result = object->Get(L"Data", 0, &data, nullptr, nullptr);
		if (FAILED(result))
		{
			// The property has no value, so ignore it
			continue;
		}
		
		// Determine which device property we are dealing with
		if (keyName == L"DEVPKEY_Device_Driver")
		{
			// Verify that the device driver value is of the expected type
			if (data.vt != VT_BSTR) {
				throw CreateError(L"DeviceDriver value was not a string");
			}
			
			// Construct the full path to the registry key for the device's driver
			details.DriverRegistryKey =
				L"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\" +
				winrt::to_hstring(data.bstrVal);
		}
		else if (keyName == L"DEVPKEY_Device_LocationPaths")
		{
			// Verify that the LocationPaths array is of the expected type
			if (data.vt != (VT_ARRAY | VT_BSTR)) {
				throw CreateError(L"LocationPaths value was not an array of strings");
			}
			
			// Retrieve the first element from the LocationPaths array
			SafeArrayIterator<BSTR> locationIterator(data.parray);
			details.LocationPath = winrt::to_hstring(*locationIterator.begin());
		}
		else if (keyName == this->devPropKeyLUID)
		{
			// Determine whether the LUID value is represented as a raw 64-bit integer or a string representation
			if (data.vt == VT_I8) {
				details.DeviceAdapter.InstanceLuid = data.llVal;
			}
			else if (data.vt == VT_BSTR)
			{
				// Parse the string back into a 64-bit integer
				details.DeviceAdapter.InstanceLuid = std::stoll(winrt::to_string(data.bstrVal));
			}
			else {
				throw CreateError(L"LUID value was not a 64-bit integer or a string");
			}
		}
	}
	
	return details;
}