Open1

ZigでWindows用のCLI作るときにやること

ryuryu

コマンドプロンプトやPowerShellでZig製のCLIを実行した場合、日本語が含まれると文字化けする。他の言語では標準入出力の文字コードを勝手にUTF-8に変更してくれるが、Zigはこれを行わない。
そのため、実行時に文字コードをUTF-8に変更し、実行終了時に元の文字コードに戻す必要がある。
下記はそのコード例。対応不十分な箇所があったらアップデートしていく。

const std = @import("std");
const builtin = @import("builtin");

const is_windows = builtin.os.tag == .windows;

pub const Output = struct {
    pub fn init() !void {
        if (comptime is_windows) {
            try WindowsOutput.init();
        }
    }
    pub fn restore() void {
        if (comptime is_windows) {
            WindowsOutput.restore();
        }
    }
};
const WindowsOutput = struct {
    const win = std.os.windows;
    const k32 = win.kernel32;
    var console_output_cp: c_uint = @as(u32, 0);

    // Make a console output code is the same as before execution
    fn setAbortSignalHandler(comptime handler: *const fn () void) !void {
        const handler_routine = struct {
            fn handler_routine(dwCtrlType: win.DWORD) callconv(win.WINAPI) win.BOOL {
                if (dwCtrlType == win.CTRL_C_EVENT) {
                    handler();
                    return win.TRUE;
                } else {
                    return win.FALSE;
                }
            }
        }.handler_routine;

        try win.SetConsoleCtrlHandler(handler_routine, true);
    }
    fn abortSignalHandler() void {
        restore();
        std.process.exit(0);
    }

    pub fn init() !void {
        // Set a console output code page to UTF-8
        const CP_UTF8 = 65001;
        console_output_cp = k32.GetConsoleOutputCP();
        try setAbortSignalHandler(abortSignalHandler);
        _ = k32.SetConsoleOutputCP(CP_UTF8);
    }
    pub fn restore() void {
        if (console_output_cp != 0)
            _ = k32.SetConsoleOutputCP(console_output_cp);
    }
};