blob: 52c4240709b38fadf4ce632c4418885289e79724 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/device/utils/setupdi_utils_win.h"
#include <ostream>
#include <string_view>
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/win/win_util.h"
namespace device {
namespace {
std::ostream& operator<<(std::ostream& os, const DEVPROPKEY& value) {
os << "{" << base::win::WStringFromGUID(value.fmtid) << ", " << value.pid
<< "}";
return os;
}
} // namespace
std::optional<std::wstring> GetDeviceStringProperty(
HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property,
device_event_log::LogType log_type) {
DEVPROPTYPE property_type;
DWORD required_size;
if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
&property_type, /*PropertyBuffer=*/nullptr,
/*PropertyBufferSize=*/0, &required_size,
/*Flags=*/0)) {
DEVICE_LOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property
<< ") unexpectedly succeeded";
return std::nullopt;
}
if (GetLastError() == ERROR_NOT_FOUND) {
return std::nullopt;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
DEVICE_PLOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property << ") failed";
return std::nullopt;
}
if (property_type != DEVPROP_TYPE_STRING) {
DEVICE_LOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property
<< ") returned unexpected type (" << property_type
<< " != " << DEVPROP_TYPE_STRING << ")";
return std::nullopt;
}
std::wstring buffer;
if (!SetupDiGetDeviceProperty(
dev_info, dev_info_data, &property, &property_type,
reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)),
required_size, /*RequiredSize=*/nullptr, /*Flags=*/0)) {
DEVICE_PLOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property << ") failed";
return std::nullopt;
}
return buffer;
}
std::optional<std::vector<std::wstring>> GetDeviceStringListProperty(
HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property,
device_event_log::LogType log_type) {
DEVPROPTYPE property_type;
DWORD required_size;
if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
&property_type, /*PropertyBuffer=*/nullptr,
/*PropertyBufferSize=*/0, &required_size,
/*Flags=*/0)) {
DEVICE_LOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property
<< ") unexpectedly succeeded";
return std::nullopt;
}
if (GetLastError() == ERROR_NOT_FOUND) {
// Simplify callers by returning an empty list when the property isn't
// found.
return std::vector<std::wstring>();
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
DEVICE_PLOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property << ") failed";
return std::nullopt;
}
if (property_type != DEVPROP_TYPE_STRING_LIST) {
DEVICE_LOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property
<< ") returned unexpected type (" << property_type
<< " != " << DEVPROP_TYPE_STRING_LIST << ")";
return std::nullopt;
}
std::wstring buffer;
if (!SetupDiGetDeviceProperty(
dev_info, dev_info_data, &property, &property_type,
reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)),
required_size, /*RequiredSize=*/nullptr, /*Flags=*/0)) {
DEVICE_PLOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property << ") failed";
return std::nullopt;
}
// Windows string list properties use a NUL character as the delimiter.
return base::SplitString(buffer, std::wstring_view(L"\0", 1),
base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
std::optional<uint32_t> GetDeviceUint32Property(
HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property,
device_event_log::LogType log_type) {
DEVPROPTYPE property_type;
uint32_t buffer;
if (!SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
&property_type,
reinterpret_cast<PBYTE>(&buffer),
sizeof(buffer), /*RequiredSize=*/nullptr,
/*Flags=*/0)) {
if (GetLastError() != ERROR_NOT_FOUND) {
DEVICE_PLOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property << ") failed";
}
return std::nullopt;
}
if (property_type != DEVPROP_TYPE_UINT32) {
DEVICE_LOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property
<< ") returned unexpected type (" << property_type
<< " != " << DEVPROP_TYPE_UINT32 << ")";
return std::nullopt;
}
return buffer;
}
std::optional<std::string> GetDeviceGuidProperty(
HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property,
device_event_log::LogType log_type) {
DEVPROPTYPE property_type;
GUID buffer;
if (!SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
&property_type,
reinterpret_cast<PBYTE>(&buffer),
sizeof(buffer), /*RequiredSize=*/nullptr,
/*Flags=*/0)) {
if (GetLastError() != ERROR_NOT_FOUND) {
DEVICE_PLOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property << ") failed";
}
return std::nullopt;
}
if (property_type != DEVPROP_TYPE_GUID) {
DEVICE_LOG(log_type, device_event_log::LOG_LEVEL_ERROR)
<< "SetupDiGetDeviceProperty(" << property
<< ") returned unexpected type (" << property_type
<< " != " << DEVPROP_TYPE_GUID << ")";
return std::nullopt;
}
return base::SysWideToUTF8(base::win::WStringFromGUID(buffer));
}
} // namespace device