Module windows_subsystem

Module windows_subsystem 

Source
Expand description

Helpers for apps built with #![windows_subsystem = "windows"].

The Windows operating system does not support hybrid CLI and GUI apps in the same executable, this module contains helpers that help provide a best effort compatibility, based on the tricks Microsoft Visual Studio uses.

The attach_console function must be called at the start of the hybrid executable when it determinate it is running in CLI mode, the build_cli_com_proxy must be called in the build script for the hybrid executable.

§Examples

The following example declares a hybrid CLI and GUI app that makes full use of this API to work on Windows.

In the main.rs file the GUI mode is the normal run and the CLI mode is a process interception defined using on_process_start!. When CLI is detected attach_console is called to enable printing.

// without this in Windows a console window opens with the GUI
#![windows_subsystem = "windows"]

fn main() {
    zng::env::init!();

    // GUI app running, in Windows this print does not output.
    println!("GUI");
    zng::APP.defaults().run(async {});
}

mod cli {
    zng::env::on_process_start!(|_| {
        // detect CLI mode
        if std::env::args().skip(1).any(|a| a.starts_with("-")) {
            // CLI app running

            // connect to stdio
            #[cfg(windows)]
            zng::env::windows_subsystem::attach_console();
            println!("CLI");

            zng::env::exit(0);
        }
    });
}

If you deploy just the above setup stdout/err will be already enabled, but there are some heavy limitations. The parent console does not know the app is connected it will return the prompt immediately and any subsequent prints are injected in the console, the user call other commands and both outputs will start mixing. To work around this you can deploy a second executable that is not built for the "windows" subsystem, this executable proxies stdio to the main executable.

In the Cargo.toml file a build dependency in zng-env is defined.

[dependencies]
zng = "0.21.8"

[target.'cfg(windows)'.build-dependencies]
zng-env = { version = "0.10.2", features = ["build_cli_com_proxy"] }

And in build.rs the build_cli_com_proxy function is called.

fn main() {
    #[cfg(windows)]
    zng_env::windows_subsystem::build_cli_com_proxy("foo.exe", None).unwrap();
}

This will build a small executable named foo.com, beside the foo.exe file. Both must be deployed to the same directory, when users call foo --args in a console window the Windows name resolution will give priory to *.com files over *.exe and call foo.com, this executable will spawn the foo.exe and proxy all stdio to it while blocking the console window because it is not built for the "windows" subsystem.

This is the same trick used by Visual Studio to support both CLI and GUI with the same devenv name.

§Full API

See zng_env::windows_subsystem for the full API.

Functions§

attach_console
Connect to parent stdio if disconnected.