Rust编写的Win32应用程序窗口标题未显示问题
解决Win32 Rust应用窗口标题不显示的问题
我帮你找到了代码里的关键问题,这会直接导致窗口标题无法显示,甚至窗口可能都没正确创建:
核心错误:CreateWindowExW调用后的多余分号
在你的main函数中,调用CreateWindowExW时末尾多了一个分号,这会让Rust认为这是一个独立语句而非表达式,导致hwnd变量被错误赋值为单元类型(),而不是实际的窗口句柄。后续的ShowWindow和UpdateWindow调用都是在无效句柄上执行,自然无法显示正常窗口(包括标题栏)。
错误代码片段:
let hwnd = unsafe { CreateWindowExW( // ... 参数省略 ... ); // 这里的分号是错误的! }
修复后的完整代码
把CreateWindowExW末尾的分号去掉,让Rust将函数返回的窗口句柄正确赋值给hwnd。同时我添加了窗口创建失败的检查逻辑,方便你排查后续可能出现的问题:
#![allow(unused)] #[link(name = "Kernel32")] extern "system" { pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; } #[link(name = "User32")] extern "system" { pub fn RegisterClassW(lpWndClass: *const WNDCLASSW) -> ATOM; pub fn GetLastError() -> DWORD; pub fn CreateWindowExW( dwExStyle: DWORD, lpClassName: LPCWSTR, lpWindowName: LPCWSTR, dwStyle: DWORD, X: c_int, Y: c_int, nWidth: c_int, nHeight: c_int, hWndParent: HWND, hMenu: HMENU, hInstance: HINSTANCE, lpParam: LPVOID, ) -> HWND; pub fn ShowWindow(hWnd: HWND, nCmdShow: c_int) -> BOOL; pub fn DefWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT; pub fn PostQuitMessage(nExitCode: c_int); pub fn DestroyWindow(hWnd: HWND) -> BOOL; pub fn GetMessageW( lpMsg: *mut MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, ) -> BOOL; pub fn TranslateMessage(lpMsg: *const MSG) -> BOOL; pub fn DispatchMessageW(lpMsg: *const MSG) -> LRESULT; pub fn UpdateWindow(hWnd: HWND) -> BOOL; } pub type c_int = i32; pub type c_uint = u32; pub type HANDLE = PVOID; pub type HBRUSH = HANDLE; pub type HCURSOR = HICON; pub type HICON = HANDLE; pub type HINSTANCE = HANDLE; pub type HWND = HANDLE; pub type LONG_PTR = isize; pub type LPARAM = LONG_PTR; pub type LPCWSTR = *const WCHAR; pub type LRESULT = LONG_PTR; pub type PVOID = *mut core::ffi::c_void; pub type UINT = c_uint; pub type UINT_PTR = usize; pub type WCHAR = wchar_t; pub type wchar_t = u16; pub type WPARAM = UINT_PTR; pub type HMODULE = HINSTANCE; pub type ATOM = WORD; pub type WORD = c_ushort; pub type c_ushort = u16; pub type DWORD = u32; pub type HMENU = HANDLE; pub type LPVOID = *mut core::ffi::c_void; pub type BOOL = c_int; pub type LONG = c_long; pub type c_long = i32; pub type WNDPROC = Option< unsafe extern "system" fn(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT, >; #[repr(C)] pub struct WNDCLASSW { pub style: UINT, pub lpfnWndProc: WNDPROC, pub cbClsExtra: c_int, pub cbWndExtra: c_int, pub hInstance: HINSTANCE, pub hIcon: HICON, pub hCursor: HCURSOR, pub hbrBackground: HBRUSH, pub lpszMenuName: LPCWSTR, pub lpszClassName: LPCWSTR, } #[repr(C)] pub struct MSG { pub hwnd: HWND, pub message: UINT, pub wParam: WPARAM, pub lParam: LPARAM, pub time: DWORD, pub pt: POINT, pub lPrivate: DWORD, } #[repr(C)] pub struct POINT { pub x: LONG, pub y: LONG, } impl Default for MSG { #[inline] #[must_use] fn default() -> Self { unsafe { core::mem::zeroed() } } } impl Default for WNDCLASSW { #[inline] #[must_use] fn default() -> Self { unsafe { core::mem::zeroed() } } } pub fn wide_null(s: &str) -> Vec<u16> { s.encode_utf16().chain(Some(0)).collect() } const WS_OVERLAPPED: u32 = 0x00000000; const WS_CAPTION: u32 = 0x00C00000; const WS_SYSMENU: u32 = 0x00080000; const WS_THICKFRAME: u32 = 0x00040000; const WS_MINIMIZEBOX: u32 = 0x00020000; const WS_MAXIMIZEBOX: u32 = 0x00010000; const WS_OVERLAPPEDWINDOW: u32 = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; const CW_USEDEFAULT: c_int = 0x80000000_u32 as c_int; const SW_SHOW: i32 = 5; const WM_NCCREATE: u32 = 0x0081; const WM_DESTROY: u32 = 0x0002; const WM_CLOSE: u32 = 0x0010; extern "system" fn window_proc(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT { unsafe { match uMsg { WM_NCCREATE => { return 1; } WM_DESTROY => { PostQuitMessage(0); return 0; } WM_CLOSE => { DestroyWindow(hwnd); return 0; } _ => { return DefWindowProcW(hwnd, uMsg, wParam, lParam); } } } } fn main() { let hInstance = unsafe { GetModuleHandleW(core::ptr::null()) }; let sample_window_class_wn = wide_null("Sample window class"); let mut wc = WNDCLASSW::default(); wc.lpfnWndProc = Some(window_proc); wc.hInstance = hInstance; wc.lpszClassName = sample_window_class_wn.as_ptr(); unsafe { if RegisterClassW(&wc) == 0 { println!( "Not able to register window class. Error code: {}", GetLastError() ); return; // 注册失败时退出,避免执行后续无效代码 } } let sample_window_name_wn = wide_null("sample window"); let hwnd = unsafe { CreateWindowExW( 0, sample_window_class_wn.as_ptr(), sample_window_name_wn.as_ptr(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, core::ptr::null_mut(), core::ptr::null_mut(), hInstance, core::ptr::null_mut(), ) // 这里移除了错误的分号 }; unsafe { if hwnd.is_null() { println!("Failed to create window. Error code: {}", GetLastError()); return; // 创建失败时退出 } ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); } let mut msg = MSG::default(); unsafe { // 传入null_mut接收所有窗口消息,是标准写法 while GetMessageW(&mut msg, core::ptr::null_mut(), 0, 0) > 0 { TranslateMessage(&msg); DispatchMessageW(&msg); } } }
额外优化说明
- 调整了
GetMessageW的参数,传入core::ptr::null_mut()可以接收所有窗口的消息,是Win32消息循环的标准写法。 - 在窗口类注册失败、窗口创建失败时添加
return语句,避免后续无效代码执行。 - 你的
WM_NCCREATE处理逻辑是正确的,返回1能让系统继续完成窗口创建流程。
现在运行cargo run,就能看到带有标题"sample window"的正常Win32窗口了。
内容的提问来源于stack exchange,提问作者taste01
相关产品推荐
相关产品推荐

