Search code examples
winapiconsolerustffi

Writing a &str to the console using winapi goes wrong when using CString


I want to write a &str to the console using WriteConsoleOutputAttribute from the winapi crate. I only have an u8 array which will be passed from the std::io::Write implementation of a struct to my winapi code.

The following does not work correctly as I see colored cells instead of text.

use std::ffi::CString;
use std::str;
use winapi::um::wincon::{
    GetConsoleScreenBufferInfo, WriteConsoleOutputAttribute, CONSOLE_SCREEN_BUFFER_INFO, COORD,
};
use winapi::um::winnt::HANDLE;

pub fn write_char_buffer(handle: HANDLE, buf: &[u8]) {
    // get buffer info
    let csbi = get_console_screen_buffer_info();

    // get string from u8[] and parse it to an c_str
    let data = str::from_utf8(buf).unwrap();
    let c_str = CString::new(data).unwrap();
    let ptr: *const u16 = (c_str.as_ptr() as *const u16);

    // get current position
    let current_pos = COORD {
        X: csbi.dwCursorPosition.X,
        Y: csbi.dwCursorPosition.Y,
    };

    let mut cells_written: u32 = 0;

    // write to console
    unsafe {
        WriteConsoleOutputAttribute(
            handle,
            ptr,
            data.len() as u32,
            current_pos,
            &mut cells_written,
        );
    }
}

pub fn get_console_screen_buffer_info() -> CONSOLE_SCREEN_BUFFER_INFO {
    let output_handle = get_output_handle();
    let mut csbi = CONSOLE_SCREEN_BUFFER_INFO::empty();
    let success;

    unsafe { success = GetConsoleScreenBufferInfo(output_handle, &mut csbi) }

    if success == 0 {
        panic!("Cannot get console screen buffer info");
    }

    csbi
}

I think that it is going wrong with the CString and the pointer. See this link for the code.

Update

Rust strings are utf8 while winapi strings are utf16. So I convert the utf8 array to utf16 String but the same output is shown, just collored cells in the console without text:

let utf8 = str::from_utf8(buf).unwrap();
let utf16: Vec<u16> = utf8.encode_utf16().collect();
let utf_string = String::from_utf16(&utf16).unwrap();
let c_str = CString::new(utf_string).unwrap();

Solution

  • I want to write a &str to the console using WriteConsoleOutputAttribute

    That's not what that function does. WriteConsoleOutputAttribute sets character attributes: things like background color, foreground color, underline. This page has a (more) complete list.

    To write text to the console, use WriteConsoleOutputCharacter after converting it to UTF-16.