如何用Rust结合winapi实现无控制台的Windows桌面GUI应用?
解决Rust+WinAPI开发GUI应用弹出控制台窗口的问题
1. 切换到Windows GUI子系统
Rust默认编译生成控制台子系统的exe,这就是启动时弹出cmd窗口的核心原因。要改成Windows GUI子系统,只需配置链接器参数:
方法1:通过build.rs配置
在项目根目录创建build.rs文件,内容如下:
fn main() { // 指定GUI子系统和入口点 println!("cargo:rustc-link-arg=/SUBSYSTEM:WINDOWS"); println!("cargo:rustc-link-arg=/ENTRY:wWinMainCRTStartup"); }
然后在Cargo.toml的[package]区块添加build = "build.rs":
[package] name = "your_app_name" version = "0.1.0" edition = "2021" build = "build.rs" # 新增这一行
方法2:直接在Cargo.toml配置profile
如果不想用build.rs,也可以在Cargo.toml的发布/开发profile中直接添加链接参数:
[profile.release] rustc_flags = [ "-C", "link-arg=/SUBSYSTEM:WINDOWS", "-C", "link-arg=/ENTRY:wWinMainCRTStartup", ] [profile.dev] rustc_flags = [ "-C", "link-arg=/SUBSYSTEM:WINDOWS", "-C", "link-arg=/ENTRY:wWinMainCRTStartup", ]
2. 实现wWinMain入口点
Windows GUI程序的标准入口是wWinMain,Rust中可以通过#[no_mangle]和extern "system"标记导出符合要求的函数,让系统正确识别。
首先在Cargo.toml添加winapi依赖:
[dependencies] winapi = { version = "0.3", features = ["winbase", "winuser", "winnls"] }
然后在src/main.rs中编写核心代码:
use winapi::{ shared::minwindef::{HINSTANCE, UINT}, um::{ winbase::STARTUPINFOW, winuser::{CreateWindowExW, DefWindowProcW, RegisterClassExW, CW_USEDEFAULT, MSG, WM_DESTROY, WM_QUIT, WS_OVERLAPPEDWINDOW}, }, }; // 导出wWinMain,遵循Windows系统调用约定 #[no_mangle] pub extern "system" fn wWinMain( h_instance: HINSTANCE, _prev_instance: HINSTANCE, _cmd_line: *const u16, cmd_show: UINT, ) -> i32 { // 注册窗口类 let class_name = to_wide("MyRustGUIWindow"); let wnd_class = RegisterClassExW(&winapi::um::winuser::WNDCLASSEXW { cbSize: std::mem::size_of::<winapi::um::winuser::WNDCLASSEXW>() as u32, style: winapi::um::winuser::CS_HREDRAW | winapi::um::winuser::CS_VREDRAW, lpfnWndProc: Some(window_proc), cbClsExtra: 0, cbWndExtra: 0, hInstance: h_instance, hIcon: unsafe { winapi::um::winuser::LoadIconW(std::ptr::null_mut(), winapi::um::winuser::IDI_APPLICATION) }, hCursor: unsafe { winapi::um::winuser::LoadCursorW(std::ptr::null_mut(), winapi::um::winuser::IDC_ARROW) }, hbrBackground: winapi::um::winuser::COLOR_WINDOW as winapi::shared::minwindef::HBRUSH, lpszMenuName: std::ptr::null(), lpszClassName: class_name.as_ptr(), hIconSm: std::ptr::null_mut(), }); if wnd_class == 0 { return 1; } // 创建主窗口 let hwnd = CreateWindowExW( 0, class_name.as_ptr(), to_wide("Rust GUI应用").as_ptr(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, std::ptr::null_mut(), std::ptr::null_mut(), h_instance, std::ptr::null_mut(), ); if hwnd.is_null() { return 1; } // 显示并更新窗口 unsafe { winapi::um::winuser::ShowWindow(hwnd, cmd_show); winapi::um::winuser::UpdateWindow(hwnd); } // 消息循环 let mut msg = MSG::default(); while unsafe { winapi::um::winuser::GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0) } != 0 { unsafe { winapi::um::winuser::TranslateMessage(&msg); winapi::um::winuser::DispatchMessageW(&msg); } } msg.wParam as i32 } // 窗口过程函数 extern "system" fn window_proc( hwnd: winapi::shared::minwindef::HWND, msg: UINT, w_param: winapi::shared::minwindef::WPARAM, l_param: winapi::shared::minwindef::LPARAM, ) -> winapi::shared::minwindef::LRESULT { match msg { WM_DESTROY => { unsafe { winapi::um::winuser::PostQuitMessage(0) }; 0 } _ => unsafe { DefWindowProcW(hwnd, msg, w_param, l_param) }, } } // 辅助函数:将UTF-8字符串转为Windows宽字符(UTF-16) fn to_wide(s: &str) -> Vec<u16> { s.encode_utf16().chain(std::iter::once(0)).collect() }
3. 编译验证
执行cargo build --release,运行target/release下的exe文件,此时只会弹出GUI窗口,不会出现控制台cmd窗口。
如果使用windows-rs crate,逻辑类似:同样需要配置链接器参数切换到GUI子系统,然后通过#[entry]宏或手动导出wWinMain实现入口。
内容的提问来源于stack exchange,提问作者Kalle Svensson
相关产品推荐
相关产品推荐

