fix(audio-info): crash when device name contains special characters (#4095)

This commit is contained in:
David Lane
2026-01-16 22:41:23 -05:00
committed by GitHub
parent fd2bfaac7e
commit ab52e27e0e
14 changed files with 476 additions and 163 deletions

View File

@@ -2,23 +2,38 @@ cmake_minimum_required(VERSION 3.20)
project(sunshine_tools)
include_directories("${CMAKE_SOURCE_DIR}")
include_directories(
"${CMAKE_SOURCE_DIR}"
"${FFMPEG_INCLUDE_DIRS}" # this is included only for logging
)
add_executable(dxgi-info dxgi.cpp)
set(TOOL_SOURCES
"${CMAKE_SOURCE_DIR}/src/logging.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/utf_utils.cpp"
)
add_executable(dxgi-info dxgi.cpp ${TOOL_SOURCES})
set_target_properties(dxgi-info PROPERTIES CXX_STANDARD 23)
target_link_libraries(dxgi-info
${Boost_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${FFMPEG_LIBRARIES} # this is included only for logging
dxgi
${PLATFORM_LIBRARIES})
libdisplaydevice::display_device # this is included only for logging
${PLATFORM_LIBRARIES}
)
target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
add_executable(audio-info audio.cpp)
add_executable(audio-info audio.cpp ${TOOL_SOURCES})
set_target_properties(audio-info PROPERTIES CXX_STANDARD 23)
target_link_libraries(audio-info
${Boost_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${FFMPEG_LIBRARIES} # this is included only for logging
libdisplaydevice::display_device # this is included only for logging
ksuser
${PLATFORM_LIBRARIES})
${PLATFORM_LIBRARIES}
)
target_compile_options(audio-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
add_executable(sunshinesvc sunshinesvc.cpp)

View File

@@ -6,17 +6,13 @@
// platform includes
#include <Audioclient.h>
#include <codecvt>
#include <iostream>
#include <locale>
#include <mmdeviceapi.h>
#include <roapi.h>
#include <synchapi.h>
// lib includes
#include <boost/locale.hpp>
// local includes
#include "src/platform/windows/utf_utils.h"
#include "src/utility.h"
DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING
@@ -35,7 +31,7 @@ namespace audio {
template<class T>
void co_task_free(T *p) {
CoTaskMemFree((LPVOID) p);
CoTaskMemFree(static_cast<LPVOID>(p));
}
using device_enum_t = util::safe_ptr<IMMDeviceEnumerator, Release<IMMDeviceEnumerator>>;
@@ -63,10 +59,6 @@ namespace audio {
PROPVARIANT prop;
};
const wchar_t *no_null(const wchar_t *str) {
return str ? str : L"Unknown";
}
struct format_t {
std::string_view name;
int channels;
@@ -118,7 +110,11 @@ namespace audio {
wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign;
if (wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
((PWAVEFORMATEXTENSIBLE) wave_format.get())->dwChannelMask = format.channel_mask;
// Access the extended format through proper offsetting
// WAVEFORMATEXTENSIBLE has WAVEFORMATEX as first member, so this is safe
const auto ext_format =
static_cast<PWAVEFORMATEXTENSIBLE>(static_cast<void *>(wave_format.get()));
ext_format->dwChannelMask = format.channel_mask;
}
}
@@ -128,7 +124,7 @@ namespace audio {
IID_IAudioClient,
CLSCTX_ALL,
nullptr,
(void **) &audio_client
static_cast<void **>(static_cast<void *>(&audio_client))
);
if (FAILED(status)) {
@@ -186,7 +182,7 @@ namespace audio {
return;
}
std::wstring device_state_string = L"Unknown"s;
std::wstring device_state_string;
switch (device_state) {
case DEVICE_STATE_ACTIVE:
device_state_string = L"Active"s;
@@ -200,28 +196,36 @@ namespace audio {
case DEVICE_STATE_NOTPRESENT:
device_state_string = L"Not present"s;
break;
default:
device_state_string = L"Unknown"s;
break;
}
std::wstring current_format = L"Unknown"s;
std::string current_format = "Unknown";
for (const auto &format : formats) {
// This will fail for any format that's not the mix format for this device,
// so we can take the first match as the current format to display.
auto audio_client = make_audio_client(device, format);
if (audio_client) {
current_format = boost::locale::conv::utf_to_utf<wchar_t>(format.name.data());
if (auto audio_client = make_audio_client(device, format)) {
current_format = std::string(format.name);
break;
}
}
std::wcout
<< L"===== Device ====="sv << std::endl
<< L"Device ID : "sv << wstring.get() << std::endl
<< L"Device name : "sv << no_null((LPWSTR) device_friendly_name.prop.pszVal) << std::endl
<< L"Adapter name : "sv << no_null((LPWSTR) adapter_friendly_name.prop.pszVal) << std::endl
<< L"Device description : "sv << no_null((LPWSTR) device_desc.prop.pszVal) << std::endl
<< L"Device state : "sv << device_state_string << std::endl
<< L"Current format : "sv << current_format << std::endl
<< std::endl;
auto safe_wstring_output = [](const wchar_t *wstr) -> std::string {
if (!wstr) {
return "Unknown";
}
return utf_utils::to_utf8(std::wstring(wstr));
};
std::cout << "===== Device =====" << std::endl;
std::cout << "Device ID : " << utf_utils::to_utf8(std::wstring(wstring.get())) << std::endl;
std::cout << "Device name : " << safe_wstring_output(device_friendly_name.prop.pwszVal) << std::endl;
std::cout << "Adapter name : " << safe_wstring_output(adapter_friendly_name.prop.pwszVal) << std::endl;
std::cout << "Device description : " << safe_wstring_output(device_desc.prop.pwszVal) << std::endl;
std::cout << "Device state : " << utf_utils::to_utf8(device_state_string) << std::endl;
std::cout << "Current format : " << current_format << std::endl;
std::cout << std::endl;
}
} // namespace audio
@@ -268,15 +272,13 @@ int main(int argc, char *argv[]) {
}
}
HRESULT status;
audio::device_enum_t device_enum;
status = CoCreateInstance(
HRESULT status = CoCreateInstance(
CLSID_MMDeviceEnumerator,
nullptr,
CLSCTX_ALL,
IID_IMMDeviceEnumerator,
(void **) &device_enum
static_cast<void **>(static_cast<void *>(&device_enum))
);
if (FAILED(status)) {

View File

@@ -3,10 +3,12 @@
* @brief Displays information about connected displays and GPUs
*/
#define WINVER 0x0A00
#include "src/platform/windows/utf_utils.h"
#include "src/utility.h"
#include <d3dcommon.h>
#include <dxgi.h>
#include <format>
#include <iostream>
using namespace std::literals;
@@ -20,17 +22,14 @@ namespace dxgi {
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
} // namespace dxgi
int main(int argc, char *argv[]) {
HRESULT status;
// Set ourselves as per-monitor DPI aware for accurate resolution values on High DPI systems
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
dxgi::factory1_t::pointer factory_p {};
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory_p);
const HRESULT status = CreateDXGIFactory1(IID_IDXGIFactory1, static_cast<void **>(static_cast<void *>(&factory_p)));
dxgi::factory1_t factory {factory_p};
if (FAILED(status)) {
std::cout << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
@@ -44,21 +43,24 @@ int main(int argc, char *argv[]) {
DXGI_ADAPTER_DESC1 adapter_desc;
adapter->GetDesc1(&adapter_desc);
std::cout
<< "====== ADAPTER ====="sv << std::endl;
std::wcout
<< L"Device Name : "sv << adapter_desc.Description << std::endl;
std::cout
<< "Device Vendor ID : 0x"sv << util::hex(adapter_desc.VendorId).to_string_view() << std::endl
<< "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl
<< "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl
<< "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl
<< "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl
<< std::endl
<< " ====== OUTPUT ======"sv << std::endl;
std::cout << "====== ADAPTER =====" << std::endl;
std::cout << "Device Name : " << utf_utils::to_utf8(std::wstring(adapter_desc.Description)) << std::endl;
std::cout << "Device Vendor ID : " << "0x" << util::hex(adapter_desc.VendorId).to_string() << std::endl;
std::cout << "Device Device ID : " << "0x" << util::hex(adapter_desc.DeviceId).to_string() << std::endl;
std::cout << "Device Video Mem : " << std::format("{} MiB", adapter_desc.DedicatedVideoMemory / 1048576) << std::endl;
std::cout << "Device Sys Mem : " << std::format("{} MiB", adapter_desc.DedicatedSystemMemory / 1048576) << std::endl;
std::cout << "Share Sys Mem : " << std::format("{} MiB", adapter_desc.SharedSystemMemory / 1048576) << std::endl;
dxgi::output_t::pointer output_p {};
bool has_outputs = false;
for (int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
// Print the header only when we find the first output
if (!has_outputs) {
std::cout << std::endl
<< " ====== OUTPUT ======" << std::endl;
has_outputs = true;
}
dxgi::output_t output {output_p};
DXGI_OUTPUT_DESC desc;
@@ -67,13 +69,11 @@ int main(int argc, char *argv[]) {
auto width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left;
auto height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top;
std::wcout
<< L" Output Name : "sv << desc.DeviceName << std::endl;
std::cout
<< " AttachedToDesktop : "sv << (desc.AttachedToDesktop ? "yes"sv : "no"sv) << std::endl
<< " Resolution : "sv << width << 'x' << height << std::endl
<< std::endl;
std::cout << " Output Name : " << utf_utils::to_utf8(std::wstring(desc.DeviceName)) << std::endl;
std::cout << " AttachedToDesktop : " << (desc.AttachedToDesktop ? "yes" : "no") << std::endl;
std::cout << " Resolution : " << std::format("{}x{}", width, height) << std::endl;
}
std::cout << std::endl;
}
return 0;