From 5e65536376ab4b2085cb34a69c6d01bb7fbf9ea8 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Tue, 14 Oct 2025 22:39:17 -0500 Subject: [PATCH] WindowsDevice: Add GetDeviceInterfaceList function and NullTerminatedStringList template. --- Source/Core/Common/WindowsDevice.cpp | 30 +++++++++++++++++++ Source/Core/Common/WindowsDevice.h | 43 ++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/Source/Core/Common/WindowsDevice.cpp b/Source/Core/Common/WindowsDevice.cpp index 405ea5e25b..9102f24b93 100644 --- a/Source/Core/Common/WindowsDevice.cpp +++ b/Source/Core/Common/WindowsDevice.cpp @@ -83,6 +83,36 @@ std::optional GetDeviceInterfaceStringProperty(LPCWSTR iface, DEVPROP_TYPE_STRING); } +NullTerminatedStringList GetDeviceInterfaceList(LPGUID iface_class_guid, DEVINSTID device_id, + ULONG flags) +{ + while (true) + { + ULONG list_size = 0; + const auto size_result = + CM_Get_Device_Interface_List_Size(&list_size, iface_class_guid, device_id, flags); + if (size_result != CR_SUCCESS || list_size == 0) + list_size = 1; + + auto buffer = std::make_unique_for_overwrite(list_size); + const auto list_result = + CM_Get_Device_Interface_List(iface_class_guid, device_id, buffer.get(), list_size, flags); + + // "A new device can be added to the system causing the size returned to no longer be valid." + // Microsoft recommends trying again in a loop. + if (list_result == CR_BUFFER_SMALL) + continue; + + if (list_result != CR_SUCCESS) + { + ERROR_LOG_FMT(COMMON, "CM_Get_Device_Interface_List: {}", list_result); + buffer[0] = 0; + } + + return {std::move(buffer)}; + } +} + } // namespace Common #endif diff --git a/Source/Core/Common/WindowsDevice.h b/Source/Core/Common/WindowsDevice.h index 463c6299b4..43acaa5092 100644 --- a/Source/Core/Common/WindowsDevice.h +++ b/Source/Core/Common/WindowsDevice.h @@ -5,6 +5,8 @@ #ifdef _WIN32 +#include +#include #include #include @@ -32,6 +34,47 @@ std::optional GetDevNodeStringProperty(DEVINST device, std::optional GetDeviceInterfaceStringProperty(LPCWSTR iface, const DEVPROPKEY* requested_property); +// Allows iterating null-separated/terminated string lists returned by the Windows API. +template +struct NullTerminatedStringList +{ + class Iterator + { + friend NullTerminatedStringList; + + public: + constexpr T* operator*() { return m_ptr; } + + constexpr Iterator& operator++() + { + m_ptr += std::basic_string_view(m_ptr).size() + 1; + return *this; + } + + constexpr Iterator operator++(int) + { + const auto result{*this}; + ++*this; + return result; + } + + constexpr bool operator==(std::default_sentinel_t) const { return *m_ptr == T{}; } + + private: + constexpr Iterator(T* ptr) : m_ptr{ptr} {} + + T* m_ptr; + }; + + constexpr auto begin() const { return Iterator{list.get()}; } + constexpr auto end() const { return std::default_sentinel; } + + std::unique_ptr list; +}; + +NullTerminatedStringList GetDeviceInterfaceList(LPGUID iface_class_guid, DEVINSTID device_id, + ULONG flags); + } // namespace Common #endif