fix(audio-info): crash when device name contains special characters (#4095)
This commit is contained in:
parent
fd2bfaac7e
commit
ab52e27e0e
14 changed files with 476 additions and 163 deletions
|
|
@ -16,10 +16,10 @@
|
|||
#include <synchapi.h>
|
||||
|
||||
// local includes
|
||||
#include "misc.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "utf_utils.h"
|
||||
|
||||
// Must be the last included file
|
||||
// clang-format off
|
||||
|
|
@ -703,7 +703,7 @@ namespace platf::audio {
|
|||
audio::wstring_t id;
|
||||
device->GetId(&id);
|
||||
|
||||
sink.host = to_utf8(id.get());
|
||||
sink.host = utf_utils::to_utf8(id.get());
|
||||
}
|
||||
|
||||
// Prepare to search for the device_id of the virtual audio sink device,
|
||||
|
|
@ -713,14 +713,14 @@ namespace platf::audio {
|
|||
if (config::audio.virtual_sink.empty()) {
|
||||
match_list = match_steam_speakers();
|
||||
} else {
|
||||
match_list = match_all_fields(from_utf8(config::audio.virtual_sink));
|
||||
match_list = match_all_fields(utf_utils::from_utf8(config::audio.virtual_sink));
|
||||
}
|
||||
|
||||
// Search for the virtual audio sink device currently present in the system.
|
||||
auto matched = find_device_id(match_list);
|
||||
if (matched) {
|
||||
// Prepare to fill virtual audio sink names with device_id.
|
||||
auto device_id = to_utf8(matched->second);
|
||||
auto device_id = utf_utils::to_utf8(matched->second);
|
||||
// Also prepend format name (basically channel layout at the moment)
|
||||
// because we don't want to extend the platform interface.
|
||||
sink.null = std::make_optional(sink_t::null_t {
|
||||
|
|
@ -736,7 +736,7 @@ namespace platf::audio {
|
|||
}
|
||||
|
||||
bool is_sink_available(const std::string &sink) override {
|
||||
const auto match_list = match_all_fields(from_utf8(sink));
|
||||
const auto match_list = match_all_fields(utf_utils::from_utf8(sink));
|
||||
const auto matched = find_device_id(match_list);
|
||||
return static_cast<bool>(matched);
|
||||
}
|
||||
|
|
@ -758,7 +758,7 @@ namespace platf::audio {
|
|||
for (const auto &format : formats) {
|
||||
auto &name = format.name;
|
||||
if (current.find(name) == 0) {
|
||||
auto device_id = from_utf8(current.substr(name.size(), current.size() - name.size()));
|
||||
auto device_id = utf_utils::from_utf8(current.substr(name.size(), current.size() - name.size()));
|
||||
return std::make_pair(device_id, std::reference_wrapper(format));
|
||||
}
|
||||
}
|
||||
|
|
@ -805,7 +805,7 @@ namespace platf::audio {
|
|||
// Sink name does not begin with virtual-(format name), hence it's not a virtual sink
|
||||
// and we don't want to change playback format of the corresponding device.
|
||||
// Also need to perform matching, sink name is not necessarily device_id in this case.
|
||||
auto matched = find_device_id(match_all_fields(from_utf8(sink)));
|
||||
auto matched = find_device_id(match_all_fields(utf_utils::from_utf8(sink)));
|
||||
if (matched) {
|
||||
return matched->second;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
#include <boost/process/v1.hpp>
|
||||
#include <MinHook.h>
|
||||
|
||||
// local includes
|
||||
#include "utf_utils.h"
|
||||
|
||||
// We have to include boost/process/v1.hpp before display.h due to WinSock.h,
|
||||
// but that prevents the definition of NTSTATUS so we must define it ourself.
|
||||
typedef long NTSTATUS;
|
||||
|
|
@ -474,8 +477,8 @@ namespace platf::dxgi {
|
|||
return -1;
|
||||
}
|
||||
|
||||
auto adapter_name = from_utf8(config::video.adapter_name);
|
||||
auto output_name = from_utf8(display_name);
|
||||
auto adapter_name = utf_utils::from_utf8(config::video.adapter_name);
|
||||
auto output_name = utf_utils::from_utf8(display_name);
|
||||
|
||||
adapter_t::pointer adapter_p;
|
||||
for (int tries = 0; tries < 2; ++tries) {
|
||||
|
|
@ -589,7 +592,7 @@ namespace platf::dxgi {
|
|||
DXGI_ADAPTER_DESC adapter_desc;
|
||||
adapter->GetDesc(&adapter_desc);
|
||||
|
||||
auto description = to_utf8(adapter_desc.Description);
|
||||
auto description = utf_utils::to_utf8(adapter_desc.Description);
|
||||
BOOST_LOG(info)
|
||||
<< std::endl
|
||||
<< "Device Description : " << description << std::endl
|
||||
|
|
@ -1068,7 +1071,7 @@ namespace platf {
|
|||
BOOST_LOG(debug)
|
||||
<< std::endl
|
||||
<< "====== ADAPTER ====="sv << std::endl
|
||||
<< "Device Name : "sv << to_utf8(adapter_desc.Description) << std::endl
|
||||
<< "Device Name : "sv << utf_utils::to_utf8(adapter_desc.Description) << std::endl
|
||||
<< "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
|
||||
|
|
@ -1084,7 +1087,7 @@ namespace platf {
|
|||
DXGI_OUTPUT_DESC desc;
|
||||
output->GetDesc(&desc);
|
||||
|
||||
auto device_name = to_utf8(desc.DeviceName);
|
||||
auto device_name = utf_utils::to_utf8(desc.DeviceName);
|
||||
|
||||
auto width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left;
|
||||
auto height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ extern "C" {
|
|||
#include "src/nvenc/nvenc_d3d11_on_cuda.h"
|
||||
#include "src/nvenc/nvenc_utils.h"
|
||||
#include "src/video.h"
|
||||
#include "utf_utils.h"
|
||||
|
||||
#if !defined(SUNSHINE_SHADERS_DIR) // for testing this needs to be defined in cmake as we don't do an install
|
||||
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/directx"
|
||||
|
|
@ -359,7 +360,7 @@ namespace platf::dxgi {
|
|||
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
#endif
|
||||
|
||||
auto wFile = from_utf8(file);
|
||||
auto wFile = utf_utils::from_utf8(file);
|
||||
auto status = D3DCompileFromFile(wFile.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypoint, shader_model, flags, 0, &compiled_p, &msg_p);
|
||||
|
||||
if (msg_p) {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
#include "utf_utils.h"
|
||||
|
||||
// UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK
|
||||
#ifndef UDP_SEND_MSG_SIZE
|
||||
|
|
@ -314,15 +315,15 @@ namespace platf {
|
|||
// Parse the environment block and populate env
|
||||
for (auto c = (PWCHAR) env_block; *c != UNICODE_NULL; c += wcslen(c) + 1) {
|
||||
// Environment variable entries end with a null-terminator, so std::wstring() will get an entire entry.
|
||||
std::string env_tuple = to_utf8(std::wstring {c});
|
||||
std::string env_tuple = utf_utils::to_utf8(std::wstring {c});
|
||||
std::string env_name = env_tuple.substr(0, env_tuple.find('='));
|
||||
std::string env_val = env_tuple.substr(env_tuple.find('=') + 1);
|
||||
|
||||
// Perform a case-insensitive search to see if this variable name already exists
|
||||
auto itr = std::find_if(env.cbegin(), env.cend(), [&](const auto &e) {
|
||||
return boost::iequals(e.get_name(), env_name);
|
||||
});
|
||||
if (itr != env.cend()) {
|
||||
if (auto itr = std::find_if(env.begin(), env.end(), [&](const auto &e) {
|
||||
return boost::iequals(e.get_name(), env_name);
|
||||
});
|
||||
itr != env.end()) {
|
||||
// Use this existing name if it is already present to ensure we merge properly
|
||||
env_name = itr->get_name();
|
||||
}
|
||||
|
|
@ -379,33 +380,36 @@ namespace platf {
|
|||
offset += wstr.length();
|
||||
}
|
||||
|
||||
std::wstring create_environment_block(bp::environment &env) {
|
||||
std::wstring create_environment_block(const bp::environment &env) {
|
||||
int size = 0;
|
||||
for (const auto &entry : env) {
|
||||
auto name = entry.get_name();
|
||||
auto value = entry.to_string();
|
||||
size += from_utf8(name).length() + 1 /* L'=' */ + from_utf8(value).length() + 1 /* L'\0' */;
|
||||
size += utf_utils::from_utf8(name).length() + 1 /* L'=' */ + utf_utils::from_utf8(value).length() + 1 /* L'\0' */;
|
||||
}
|
||||
|
||||
size += 1 /* L'\0' */;
|
||||
|
||||
wchar_t env_block[size];
|
||||
std::vector<wchar_t> env_block(size);
|
||||
int offset = 0;
|
||||
for (const auto &entry : env) {
|
||||
auto name = entry.get_name();
|
||||
auto value = entry.to_string();
|
||||
|
||||
// Construct the NAME=VAL\0 string
|
||||
append_string_to_environment_block(env_block, offset, from_utf8(name));
|
||||
env_block[offset++] = L'=';
|
||||
append_string_to_environment_block(env_block, offset, from_utf8(value));
|
||||
env_block[offset++] = L'\0';
|
||||
append_string_to_environment_block(env_block.data(), offset, utf_utils::from_utf8(name));
|
||||
env_block[offset] = L'=';
|
||||
offset++;
|
||||
append_string_to_environment_block(env_block.data(), offset, utf_utils::from_utf8(value));
|
||||
env_block[offset] = L'\0';
|
||||
offset++;
|
||||
}
|
||||
|
||||
// Append a final null terminator
|
||||
env_block[offset++] = L'\0';
|
||||
env_block[offset] = L'\0';
|
||||
offset++;
|
||||
|
||||
return std::wstring(env_block, offset);
|
||||
return std::wstring(env_block.data(), offset);
|
||||
}
|
||||
|
||||
LPPROC_THREAD_ATTRIBUTE_LIST allocate_proc_thread_attr_list(DWORD attribute_count) {
|
||||
|
|
@ -676,14 +680,14 @@ namespace platf {
|
|||
* @return A command string suitable for use by CreateProcess().
|
||||
*/
|
||||
std::wstring resolve_command_string(const std::string &raw_cmd, const std::wstring &working_dir, HANDLE token, DWORD &creation_flags) {
|
||||
std::wstring raw_cmd_w = from_utf8(raw_cmd);
|
||||
std::wstring raw_cmd_w = utf_utils::from_utf8(raw_cmd);
|
||||
|
||||
// First, convert the given command into parts so we can get the executable/file/URL without parameters
|
||||
auto raw_cmd_parts = boost::program_options::split_winmain(raw_cmd_w);
|
||||
if (raw_cmd_parts.empty()) {
|
||||
// This is highly unexpected, but we'll just return the raw string and hope for the best.
|
||||
BOOST_LOG(warning) << "Failed to split command string: "sv << raw_cmd;
|
||||
return from_utf8(raw_cmd);
|
||||
return utf_utils::from_utf8(raw_cmd);
|
||||
}
|
||||
|
||||
auto raw_target = raw_cmd_parts.at(0);
|
||||
|
|
@ -697,7 +701,7 @@ namespace platf {
|
|||
res = UrlGetPartW(raw_target.c_str(), scheme.data(), &out_len, URL_PART_SCHEME, 0);
|
||||
if (res != S_OK) {
|
||||
BOOST_LOG(warning) << "Failed to extract URL scheme from URL: "sv << raw_target << " ["sv << util::hex(res).to_string_view() << ']';
|
||||
return from_utf8(raw_cmd);
|
||||
return utf_utils::from_utf8(raw_cmd);
|
||||
}
|
||||
|
||||
// If the target is a URL, the class is found using the URL scheme (prior to and not including the ':')
|
||||
|
|
@ -708,13 +712,13 @@ namespace platf {
|
|||
if (extension == nullptr || *extension == 0) {
|
||||
// If the file has no extension, assume it's a command and allow CreateProcess()
|
||||
// to try to find it via PATH
|
||||
return from_utf8(raw_cmd);
|
||||
return utf_utils::from_utf8(raw_cmd);
|
||||
} else if (boost::iequals(extension, L".exe")) {
|
||||
// If the file has an .exe extension, we will bypass the resolution here and
|
||||
// directly pass the unmodified command string to CreateProcess(). The argument
|
||||
// escaping rules are subtly different between CreateProcess() and ShellExecute(),
|
||||
// and we want to preserve backwards compatibility with older configs.
|
||||
return from_utf8(raw_cmd);
|
||||
return utf_utils::from_utf8(raw_cmd);
|
||||
}
|
||||
|
||||
// For regular files, the class is found using the file extension (including the dot)
|
||||
|
|
@ -731,7 +735,7 @@ namespace platf {
|
|||
|
||||
// Override HKEY_CLASSES_ROOT and HKEY_CURRENT_USER to ensure we query the correct class info
|
||||
if (!override_per_user_predefined_keys(token)) {
|
||||
return from_utf8(raw_cmd);
|
||||
return utf_utils::from_utf8(raw_cmd);
|
||||
}
|
||||
|
||||
// Find the command string for the specified class
|
||||
|
|
@ -762,7 +766,7 @@ namespace platf {
|
|||
|
||||
if (res != S_OK) {
|
||||
BOOST_LOG(warning) << "Failed to query command string for raw command: "sv << raw_cmd << " ["sv << util::hex(res).to_string_view() << ']';
|
||||
return from_utf8(raw_cmd);
|
||||
return utf_utils::from_utf8(raw_cmd);
|
||||
}
|
||||
|
||||
// Finally, construct the real command string that will be passed into CreateProcess().
|
||||
|
|
@ -896,7 +900,7 @@ namespace platf {
|
|||
* @return A `bp::child` object representing the new process, or an empty `bp::child` object if the launch fails.
|
||||
*/
|
||||
bp::child run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
std::wstring start_dir = from_utf8(working_dir.string());
|
||||
std::wstring start_dir = utf_utils::from_utf8(working_dir.string());
|
||||
HANDLE job = group ? group->native_handle() : nullptr;
|
||||
STARTUPINFOEXW startup_info = create_startup_info(file, job ? &job : nullptr, ec);
|
||||
PROCESS_INFORMATION process_info;
|
||||
|
|
@ -1690,65 +1694,13 @@ namespace platf {
|
|||
return {};
|
||||
}
|
||||
|
||||
std::wstring from_utf8(const std::string &string) {
|
||||
// No conversion needed if the string is empty
|
||||
if (string.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the output size required to store the string
|
||||
auto output_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), string.size(), nullptr, 0);
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to get UTF-16 buffer size: "sv << winerr;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Perform the conversion
|
||||
std::wstring output(output_size, L'\0');
|
||||
output_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), string.size(), output.data(), output.size());
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to convert string to UTF-16: "sv << winerr;
|
||||
return {};
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string to_utf8(const std::wstring &string) {
|
||||
// No conversion needed if the string is empty
|
||||
if (string.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the output size required to store the string
|
||||
auto output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to get UTF-8 buffer size: "sv << winerr;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Perform the conversion
|
||||
std::string output(output_size, '\0');
|
||||
output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), output.data(), output.size(), nullptr, nullptr);
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to convert string to UTF-8: "sv << winerr;
|
||||
return {};
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string get_host_name() {
|
||||
WCHAR hostname[256];
|
||||
if (GetHostNameW(hostname, ARRAYSIZE(hostname)) == SOCKET_ERROR) {
|
||||
BOOST_LOG(error) << "GetHostNameW() failed: "sv << WSAGetLastError();
|
||||
return "Sunshine"s;
|
||||
}
|
||||
return to_utf8(hostname);
|
||||
return utf_utils::to_utf8(hostname);
|
||||
}
|
||||
|
||||
class win32_high_precision_timer: public high_precision_timer {
|
||||
|
|
|
|||
|
|
@ -19,18 +19,4 @@ namespace platf {
|
|||
int64_t qpc_counter();
|
||||
|
||||
std::chrono::nanoseconds qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2);
|
||||
|
||||
/**
|
||||
* @brief Convert a UTF-8 string into a UTF-16 wide string.
|
||||
* @param string The UTF-8 string.
|
||||
* @return The converted UTF-16 wide string.
|
||||
*/
|
||||
std::wstring from_utf8(const std::string &string);
|
||||
|
||||
/**
|
||||
* @brief Convert a UTF-16 wide string into a UTF-8 string.
|
||||
* @param string The UTF-16 wide string.
|
||||
* @return The converted UTF-8 string.
|
||||
*/
|
||||
std::string to_utf8(const std::wstring &string);
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "src/nvhttp.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/thread_safe.h"
|
||||
#include "utf_utils.h"
|
||||
|
||||
#define _FN(x, ret, args) \
|
||||
typedef ret(*x##_fn) args; \
|
||||
|
|
@ -109,8 +110,8 @@ namespace platf::publish {
|
|||
std::wstring domain {SERVICE_TYPE_DOMAIN.data(), SERVICE_TYPE_DOMAIN.size()};
|
||||
|
||||
auto hostname = platf::get_host_name();
|
||||
auto name = from_utf8(net::mdns_instance_name(hostname) + '.') + domain;
|
||||
auto host = from_utf8(hostname + ".local");
|
||||
auto name = utf_utils::from_utf8(net::mdns_instance_name(hostname) + '.') + domain;
|
||||
auto host = utf_utils::from_utf8(hostname + ".local");
|
||||
|
||||
DNS_SERVICE_INSTANCE instance {};
|
||||
instance.pszInstanceName = name.data();
|
||||
|
|
|
|||
66
src/platform/windows/utf_utils.cpp
Normal file
66
src/platform/windows/utf_utils.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @file src/platform/windows/utf_utils.cpp
|
||||
* @brief Minimal UTF conversion utilities for Windows tools
|
||||
*/
|
||||
#include "utf_utils.h"
|
||||
|
||||
#include "src/logging.h"
|
||||
|
||||
#include <string>
|
||||
#include <Windows.h>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace utf_utils {
|
||||
std::wstring from_utf8(const std::string &string) {
|
||||
// No conversion needed if the string is empty
|
||||
if (string.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the output size required to store the string
|
||||
auto output_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), string.size(), nullptr, 0);
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to get UTF-16 buffer size: "sv << winerr;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Perform the conversion
|
||||
std::wstring output(output_size, L'\0');
|
||||
output_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), string.size(), output.data(), output.size());
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to convert string to UTF-16: "sv << winerr;
|
||||
return {};
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string to_utf8(const std::wstring &string) {
|
||||
// No conversion needed if the string is empty
|
||||
if (string.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the output size required to store the string
|
||||
auto output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to get UTF-8 buffer size: "sv << winerr;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Perform the conversion
|
||||
std::string output(output_size, '\0');
|
||||
output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), output.data(), output.size(), nullptr, nullptr);
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to convert string to UTF-8: "sv << winerr;
|
||||
return {};
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
} // namespace utf_utils
|
||||
23
src/platform/windows/utf_utils.h
Normal file
23
src/platform/windows/utf_utils.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @file src/platform/windows/utf_utils.h
|
||||
* @brief Minimal UTF conversion utilities for Windows tools
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace utf_utils {
|
||||
/**
|
||||
* @brief Convert a UTF-8 string into a UTF-16 wide string.
|
||||
* @param string The UTF-8 string.
|
||||
* @return The converted UTF-16 wide string.
|
||||
*/
|
||||
std::wstring from_utf8(const std::string &string);
|
||||
|
||||
/**
|
||||
* @brief Convert a UTF-16 wide string into a UTF-8 string.
|
||||
* @param string The UTF-16 wide string.
|
||||
* @return The converted UTF-8 string.
|
||||
*/
|
||||
std::string to_utf8(const std::wstring &string);
|
||||
} // namespace utf_utils
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
// from_utf8() string conversion function
|
||||
#include "platform/windows/misc.h"
|
||||
#include "platform/windows/utf_utils.h"
|
||||
|
||||
// _SH constants for _wfsopen()
|
||||
#include <share.h>
|
||||
|
|
@ -183,7 +183,7 @@ namespace proc {
|
|||
#ifdef _WIN32
|
||||
// fopen() interprets the filename as an ANSI string on Windows, so we must convert it
|
||||
// to UTF-16 and use the wchar_t variants for proper Unicode log file path support.
|
||||
auto woutput = platf::from_utf8(_app.output);
|
||||
auto woutput = utf_utils::from_utf8(_app.output);
|
||||
|
||||
// Use _SH_DENYNO to allow us to open this log file again for writing even if it is
|
||||
// still open from a previous execution. This is required to handle the case of a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue