Can you make a Windows desktop app in Rust and winapi?

1.8k views Asked by At

I've got a Windows application with a GUI written in Rust and winapi. Despite its GUI, it behaves like a console application. When the exe file is started, a Command Prompt window pops up, and the application is run from it. This is not what I want; a main window should open instead, as in all real desktop apps. How can I achieve this goal in Rust with winapi?

I've investigated some options. You can develop Windows desktop applications using Tauri or gtk-rs, but both of these techniques have drawbacks when used for Windows apps. More options may be found here. I've also tried the windows-rs samples available on the internet, but they're all console apps with a graphical user interface, which isn't what I'm looking for.

I also note that C++ desktop applications use the function int APIENTRY wWinMain(...) as the entry point while console applications use int main(...), and wWinMain doesn't seem available in rust winapi.

1

There are 1 answers

1
IInspectable On BEST ANSWER

Whether the system allocates a console for a newly created process is controlled by the Subsystem field in the Windows-specific optional PE header. The field is populated through the linker's /SUBSYSTEM command line option. The only relevant arguments for desktop applications are CONSOLE and WINDOWS. The former instructs the system to allocate a console on launch, whereas the latter won't.

You can instruct the linker to target the WINDOWS subsystem from Rust code by placing the per-module

#![windows_subsystem = "windows"]

attribute (see windows-subsystem) inside the main module of your application.

You'll find an example of this in the core_app sample of the windows crate.

This is the most convenient way to target the WINDOWS subsystem. You can also explicitly pass the linker flag along, e.g. by placing the following override into .cargo/config.toml:

[build]
rustflags = [
    "-C", "link-arg=/SUBSYSTEM:WINDOWS",
]

This may or may not work, depending on the linker you happen to be using. Since the linker isn't part of the Rust toolchain, making sure that this works and has the intended effect is on you.


A note on the entry point's function name: It is irrelevant as far as the OS loader is concerned. It never even makes it into the final executable image anyway. The PE image only stores the (image-base-relative) AddressOfEntryPoint, and that symbol could have been named anything.

The concrete name is only relevant to the build tools involved in generating the respective linker input.

More info here: WinMain is just the conventional name for the Win32 process entry point. The underlying principles apply to Rust just the same, particularly the aspect that the user-defined entry point (fn main()) isn't actually the executable's entry point.