Skip to content

Commit b59c8ec

Browse files
committed
Add std.os.windows.setConsoleCodePage (implemented in terms of ntdll)
This allows us to get the functionality of kernel32.SetConsoleOutputCP/kernel32.SetConsoleCP but without the dependency on kernel32.dll.
1 parent ae6a989 commit b59c8ec

File tree

1 file changed

+66
-0
lines changed

1 file changed

+66
-0
lines changed

lib/std/os/windows.zig

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,72 @@ pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid:
20902090
return function;
20912091
}
20922092

2093+
/// https://github.com/microsoft/terminal/blob/931aa8c87e5f5dbd28cf838bee450d082474a732/dep/Console/conmsgl1.h#L117-L120
2094+
const CONSOLE_MSG_HEADER = extern struct {
2095+
ApiNumber: ULONG,
2096+
ApiDescriptorSize: ULONG,
2097+
};
2098+
2099+
pub const CP_UTF8 = 65001;
2100+
2101+
/// Like the kernel32 functions SetConsoleOutputCP/SetConsoleCP, but implemented using ntdll.NtDeviceIoControlFile.
2102+
pub fn setConsoleCodePage(code_page: ULONG, direction: enum { input, output }) DeviceIoControlError!void {
2103+
const console_handle = peb().ProcessParameters.ConsoleHandle;
2104+
// The final control code should be 0x00500016 which means that the function is 0x16 >> 2 or 5.
2105+
const console_set_cp_function = 5;
2106+
// Note: The NtDeviceIoControlFile call will fail with 0xc0000010 ['Incorrect function.'] if the method is
2107+
// anything other than METHOD_OUT_DIRECT.
2108+
// TODO: Understand why METHOD_OUT_DIRECT is required.
2109+
const io_control_code = comptime CTL_CODE(FILE_DEVICE_CONSOLE, console_set_cp_function, .METHOD_OUT_DIRECT, FILE_ANY_ACCESS);
2110+
comptime std.debug.assert(io_control_code == 0x00500016);
2111+
2112+
// This is actually one value in an enum but we just care about this value
2113+
// https://github.com/microsoft/terminal/blob/931aa8c87e5f5dbd28cf838bee450d082474a732/dep/Console/conmsgl2.h#L152-L152
2114+
// https://github.com/microsoft/terminal/blob/931aa8c87e5f5dbd28cf838bee450d082474a732/dep/Console/conmsgl1.h#L29-L30
2115+
const ConsolepSetCP = (2 << 24) + 4;
2116+
2117+
// https://github.com/microsoft/terminal/blob/931aa8c87e5f5dbd28cf838bee450d082474a732/dep/Console/conmsgl2.h#L52-L55
2118+
const CONSOLE_SETCP_MSG = extern struct {
2119+
CodePage: ULONG,
2120+
Output: bool,
2121+
};
2122+
2123+
// https://github.com/microsoft/terminal/blob/931aa8c87e5f5dbd28cf838bee450d082474a732/dep/Console/conmsgl2.h#L196-L201
2124+
const CONSOLE_MSG_L2 = extern struct {
2125+
Header: CONSOLE_MSG_HEADER,
2126+
// This is actually a union of other types but we just care about this for now
2127+
Body: CONSOLE_SETCP_MSG,
2128+
};
2129+
2130+
// The _unknown fields of this struct have unknown usage/importance/size. These values were
2131+
// obtained by looking at the `input` memory passed to the NtDeviceIoControlFile call
2132+
// that occurs from a successful call of `kernel32.SetConsoleOutputCP()`.
2133+
const UNKNOWN_IOCTL_INPUT = extern struct {
2134+
_unknown1: u32 = 0,
2135+
_unknown2: u32 = 0,
2136+
_unknown3: u32 = 1,
2137+
_unknown4: u32 = 1,
2138+
_unknown5: u32 = 0x10,
2139+
_unknown6: u32 = 0x20,
2140+
header: *CONSOLE_MSG_HEADER,
2141+
_unknown7: u32 = 0x8,
2142+
_unknown8: u32 = 0,
2143+
body: *CONSOLE_SETCP_MSG,
2144+
};
2145+
2146+
// Note: These must be var to ensure the memory gets put on the stack.
2147+
var console_msg = CONSOLE_MSG_L2{
2148+
.Header = .{ .ApiNumber = ConsolepSetCP, .ApiDescriptorSize = @sizeOf(CONSOLE_SETCP_MSG) },
2149+
.Body = .{ .CodePage = code_page, .Output = direction == .output },
2150+
};
2151+
var input = UNKNOWN_IOCTL_INPUT{
2152+
.header = &console_msg.Header,
2153+
.body = &console_msg.Body,
2154+
};
2155+
2156+
return DeviceIoControl(console_handle, io_control_code, std.mem.asBytes(&input), null);
2157+
}
2158+
20932159
/// Call this when you made a windows DLL call or something that does SetLastError
20942160
/// and you get an unexpected error.
20952161
pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {

0 commit comments

Comments
 (0)