Skip to content

Commit 1a44c18

Browse files
ChrisDentongitbot
authored and
gitbot
committed
Windows: Use WriteFile to write to a UTF-8 console
1 parent 63c9e83 commit 1a44c18

File tree

3 files changed

+26
-1
lines changed

3 files changed

+26
-1
lines changed

std/src/sys/pal/windows/c/bindings.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2426,6 +2426,7 @@ Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_PROCESSING
24262426
Windows.Win32.System.Console.ENABLE_WINDOW_INPUT
24272427
Windows.Win32.System.Console.ENABLE_WRAP_AT_EOL_OUTPUT
24282428
Windows.Win32.System.Console.GetConsoleMode
2429+
Windows.Win32.System.Console.GetConsoleOutputCP
24292430
Windows.Win32.System.Console.GetStdHandle
24302431
Windows.Win32.System.Console.ReadConsoleW
24312432
Windows.Win32.System.Console.STD_ERROR_HANDLE

std/src/sys/pal/windows/c/windows_sys.rs

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ windows_targets::link!("kernel32.dll" "system" fn FreeEnvironmentStringsW(penv :
3434
windows_targets::link!("kernel32.dll" "system" fn GetActiveProcessorCount(groupnumber : u16) -> u32);
3535
windows_targets::link!("kernel32.dll" "system" fn GetCommandLineW() -> PCWSTR);
3636
windows_targets::link!("kernel32.dll" "system" fn GetConsoleMode(hconsolehandle : HANDLE, lpmode : *mut CONSOLE_MODE) -> BOOL);
37+
windows_targets::link!("kernel32.dll" "system" fn GetConsoleOutputCP() -> u32);
3738
windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferlength : u32, lpbuffer : PWSTR) -> u32);
3839
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE);
3940
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32);
@@ -3333,6 +3334,7 @@ pub struct XSAVE_FORMAT {
33333334
pub XmmRegisters: [M128A; 8],
33343335
pub Reserved4: [u8; 224],
33353336
}
3337+
33363338
#[cfg(target_arch = "arm")]
33373339
#[repr(C)]
33383340
pub struct WSADATA {

std/src/sys/pal/windows/stdio.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,43 @@ fn is_console(handle: c::HANDLE) -> bool {
8484
unsafe { c::GetConsoleMode(handle, &mut mode) != 0 }
8585
}
8686

87+
/// Returns true if the attached console's code page is currently UTF-8.
88+
#[cfg(not(target_vendor = "win7"))]
89+
fn is_utf8_console() -> bool {
90+
unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 }
91+
}
92+
93+
#[cfg(target_vendor = "win7")]
94+
fn is_utf8_console() -> bool {
95+
// Windows 7 has a fun "feature" where WriteFile on a console handle will return
96+
// the number of UTF-16 code units written and not the number of bytes from the input string.
97+
// So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code.
98+
false
99+
}
100+
87101
fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result<usize> {
88102
if data.is_empty() {
89103
return Ok(0);
90104
}
91105

92106
let handle = get_handle(handle_id)?;
93-
if !is_console(handle) {
107+
if !is_console(handle) || is_utf8_console() {
94108
unsafe {
95109
let handle = Handle::from_raw_handle(handle);
96110
let ret = handle.write(data);
97111
let _ = handle.into_raw_handle(); // Don't close the handle
98112
return ret;
99113
}
114+
} else {
115+
write_console_utf16(data, incomplete_utf8, handle)
100116
}
117+
}
101118

119+
fn write_console_utf16(
120+
data: &[u8],
121+
incomplete_utf8: &mut IncompleteUtf8,
122+
handle: c::HANDLE,
123+
) -> io::Result<usize> {
102124
if incomplete_utf8.len > 0 {
103125
assert!(
104126
incomplete_utf8.len < 4,

0 commit comments

Comments
 (0)