You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
        }
    }
}

额外优化说明

  1. 调整了GetMessageW的参数,传入core::ptr::null_mut()可以接收所有窗口的消息,是Win32消息循环的标准写法。
  2. 在窗口类注册失败、窗口创建失败时添加return语句,避免后续无效代码执行。
  3. 你的WM_NCCREATE处理逻辑是正确的,返回1能让系统继续完成窗口创建流程。

现在运行cargo run,就能看到带有标题"sample window"的正常Win32窗口了。

内容的提问来源于stack exchange,提问作者taste01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 21:08:14