windowswinapirust

Child Windows using Rust


I'm trying to create a window form using a standard Windows API window and child windows as controls (most specifically Static Text, Text Box and a Button).

I'm using the windows crate and the code compiles without any errors and runs, but I can see only the blank window, not the controls inside.

What I'm doing wrong?

Thanks in advance!

#![allow(non_snake_case)]

use windows::{
    core::*, 
    Win32::Foundation::*, 
    Win32::Graphics::Gdi::*,
    Win32::System::LibraryLoader::GetModuleHandleA, 
    Win32::UI::WindowsAndMessaging::*,
};


fn main() -> Result<()> {
    unsafe {
        let instance = GetModuleHandleA(None)?;
        let window_class = s!("window");

        let wc = WNDCLASSA {
            hCursor: LoadCursorW(None, IDC_ARROW)?,
            hInstance: instance.into(),
            lpszClassName: window_class,
            style: CS_HREDRAW | CS_VREDRAW,
            lpfnWndProc: Some(wndproc),
            ..Default::default()
        };

        let atom = RegisterClassA(&wc);
        debug_assert!(atom != 0);

        let _hwnd = CreateWindowExA(
            WINDOW_EX_STYLE::default(),
            window_class,
            s!("A simple Form"),
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            None,
            None,
            instance,
            None,
        )?;

    let _hndLabel = CreateWindowExA(
               WINDOW_EX_STYLE::default(),
               s!("static"), 
               s!("What's your name? "),
               WINDOW_STYLE::default(),
                           50, 100, 200, 30, _hwnd, None, instance, None)?;

    let _hndCaixa = CreateWindowExA(
                WINDOW_EX_STYLE::default(),
                s!("edit"), 
                s!("Type Here!"),
                WINDOW_STYLE::default(),
                            200, 100, 200, 30, _hwnd, None, instance, None)?;

    let _hndButton = CreateWindowExA(
                WINDOW_EX_STYLE::default(),
                s!("Button"), 
                s!("  OK  "),
                WINDOW_STYLE::default(),
                            400, 100, 200, 30, _hwnd, None, instance, None)?;

        let _ = ShowWindow(_hwnd, SW_SHOW);
    let _ = UpdateWindow(_hwnd);

        let mut message = MSG::default();

        while GetMessageA(&mut message, None, 0, 0).into() {
            DispatchMessageA(&message);
        }

        Ok(())
    }
}

extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
    unsafe {
        match message {
            WM_PAINT => {
                println!("WM_PAINT");
                _ = ValidateRect(window, None);

                LRESULT(0)
            }
            WM_DESTROY => {
                println!("WM_DESTROY");
                PostQuitMessage(0);
                LRESULT(0)
            }
            _ => DefWindowProcA(window, message, wparam, lparam),
        }
    }
}

Solution

  • There are multiple issues with how you create the windows, as you do not provide sensible window styles.

    First, the main window gets WS_OVERLAPPEDWINDOW | WS_VISIBLE. You do not want to pass WS_VISIBLE to the main window before creating the children, because then, if you create enough of them, you could see them flickering into existence. You should first create the main window as invisible, then all its children, and then finally use ShowWindow to make the main window visible. (You do the last part even though the window already is visible.)

    Second, all your child windows aren't actually child windows, because you don't pass the WS_CHILD style. This means they're just owned top-level windows.

    Third, your child windows aren't visible because you neither pass the WS_VISIBLE style nor call ShowWindow on them. You want to pass the style, because if the main windows is invisible, having the child "visible" just means it appears together with the main window as that gets finally shown.