We previously created a Rust version of the classic C++ “Your First Windows Program” using the windows crate in the post win32 with rust: simple windows. While the final result worked well, all win32 API calls in that example are unsafe. There is nothing wrong with that, but are there any safe alternatives?

Thankfully, the winsafe crate offers a safe abstraction over win32 API calls. It also provides higher-level, idiomatic, and opinioned abstractions for working with win32 subsystems.

Long story short, the previous example can be rewritten using winsafe as follows (though there are some extra details happening behind the scenes compared to the original):

use winsafe::{
    co::COLOR,
    gui::{self, Brush},
    prelude::{gdi_Hdc, user_Hwnd, GuiParent, GuiWindow},
};

fn main() -> winsafe::AnyResult<()> {
    let wnd = gui::WindowMain::new(gui::WindowMainOpts {
        title: "Learn to Program Windows".to_owned(),
        ..Default::default()
    });

    let wnd2 = wnd.clone();
    wnd.on().wm_paint(move || -> winsafe::AnyResult<()> {
        let dc = wnd2.hwnd().BeginPaint()?;

        let brush = Brush::Color(COLOR::WINDOWFRAME);
        dc.FillRect(dc.paintstruct().rcPaint, &brush.as_hbrush())?;

        Ok(())
    });

    wnd.run_main(None)?;

    Ok(())
}

The gui::WindowMain now encapsulates the window state and associated event handling. Event handling is solved elegantly via closures, making code more granular. Finally, the run_main method does everything we had to do before from registering the window class registration to running the event loop.

Astractions are great, but what if we need to change the event loop to be non-blocking? In the C++ or windows crate example, we could replace GetMessageW with PeekMessageW and rewrite the loop slightly to implement a popular pattern used in game development:

    let mut message = MSG::default();
    unsafe {
        loop {
            while PeekMessageW(&mut message, None, 0, 0, PM_REMOVE).into() {
                _ = TranslateMessage(&message);
                DispatchMessageW(&message);
            }

            if message.message == WM_QUIT {
                break;
            }

            // Run our custom code
        }
    }

Unfortunately, implementing this with winsafe’s gui::WindowMain is not straightforward. The current run_main implementation tightly couples the event loop, so to change the structure of the event loop we would not only need to rewrite run_main, but replicate much of the functionality of the original example. That’s fine, this abstraction despite being incredibly powerful and convenient, is not a silver bullet.