And for still show output to stderr / stdout I use AttachConsole from WinAPI like that:
#[cfg(windows)]
pub fn attach_console() {
use windows::Win32::System::Console::{AttachConsole, ATTACH_PARENT_PROCESS};
let _ = unsafe { AttachConsole(ATTACH_PARENT_PROCESS) };
}
Then println! and eprintln! show output in Windows console.
The problem is that if I call c++ function from Rust that writes to stdout/stderr it doesn't show the output in the console in gui mode (release mode in our case due to cfg_attr).
I also created example for better understanding the issue:
Since rust doesn't catch foreign exceptions it's crucial for me at least to catch stderr logs from c++ functions. I hope that it's possible to get it into the console in GUI mode.
The MSVC logging utilities for C++ generally don't output to stderr, but instead to the attached debugger (if present), which is a different output device.
I added the following and now it prints to stderr/stdout both from c++ or from Rust
use windows::Win32::System::Console::{AttachConsole, ATTACH_PARENT_PROCESS};
use std::ffi::CString;
unsafe {
let _ = AttachConsole(ATTACH_PARENT_PROCESS);
let conout = CString::new("CONOUT$").expect("CString::new failed");
let stdout = libc_stdhandle::stdout();
let stderr = libc_stdhandle::stderr();
let mode = CString::new("w").unwrap();
libc::freopen(conout.as_ptr(), mode.as_ptr(), stdout);
libc::freopen(conout.as_ptr(), mode.as_ptr(), stderr);
}
Is it ok to use it like that in release mode or it can possibly fail and crash the program?
CONOUT$ might not exist if there's no console in the parent process either. You might want to check for an error in AttachConsole and use AllocConsole() to create a new console window.
You can also use SetStdHandle() instead of freopen() for potentially slightly smaller code, since you already are pulling in the windows crate.
Yeah, you just need the constants from the documentation, eg STD_OUTPUT_HANDLE, for which standard handle you are setting, and the file handle you are setting it to. You can either use raw CreateFile() for that, a but more fiddling to get the right parameters, or just grab the raw handle out of a std::fs::File with, eg AsRawHandle in std::os::windows::io - Rust, though be careful about the File closing that handle when it gets dropped.
The c++ standard streams are managed internally by the c runtime library. SetStdHandle affects other WinAPI calls that use the handle, but not the c++ runtime's streams. That's why I need to use freopen() or similar functions from libc to change the c++ runtime streams.
Ah, that would make some sense that libc has an internal copy of the old handles. I basically never used libc to print even when using C, so I never ran into that.