Skip to main content

hello_world/
hello-world.rs

1#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
2#![forbid(unsafe_op_in_unsafe_fn)]
3
4use hwnd::*;
5
6use abistr::cstr16;
7
8use bytemuck::*;
9
10use winapi::um::winuser::*;
11
12use winresult::ERROR;
13
14use std::ptr::*;
15use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
16
17
18
19fn main() {
20    // To ensure any Drop s are run before calling exit, dispatch
21    // all logic to an inner implementation function:
22    std::process::exit(main_imp())
23}
24
25fn main_imp() -> i32 {
26    let hinstance = get_module_handle_entry_exe().unwrap();
27    let hcursor = load_cursor_w(None, IDC::ARROW).unwrap();
28    let hicon   = load_icon_w(None, IDI::APPLICATION).unwrap();
29
30    let wc = WndClassW {
31        wnd_proc: Some(window_proc),
32        hinstance,
33        hcursor,
34        hicon,
35        class_name: cstr16!("SampleWndClass").into(),
36        .. WndClassW::zeroed()
37    };
38    let wc = unsafe { register_class_w(&wc) }.unwrap();
39
40    let ex_style = 0;
41    let style = WS::OVERLAPPEDWINDOW;
42    let size = adjust_window_rect_ex_copy(
43        Rect { left: 0, right: 800, top: 0, bottom: 600 },
44        style, false, ex_style
45    ).unwrap();
46
47    let hwnd = unsafe { create_window_ex_w(
48        ex_style,
49        wc,
50        cstr16!("hello-world"),
51        style,
52        CW_USEDEFAULT,
53        CW_USEDEFAULT,
54        size.right - size.left,
55        size.bottom - size.top,
56        null_mut(),
57        null_mut(),
58        hinstance,
59        null_mut(),
60    )}.unwrap();
61
62    show_window_async(hwnd, SW::SHOWNORMAL).unwrap();
63
64    let mut msg = Msg::zeroed();
65    while get_message_w(&mut msg, HWnd::NULL, 0, 0).unwrap() {
66        translate_message(&msg);
67        let _ = unsafe { dispatch_message_w(&msg) };
68    }
69
70    if msg.message == WM::QUIT { return msg.wparam as _ }
71    0 // assume success of we ever `break` out of our message loop instead of `return !0`ing
72}
73
74/// ### ⚠️ Safety ⚠️
75/// *   `hwnd` must be a valid window
76/// *   `wparam` / `lparam` may be assumed to be valid pointers depending no the exact `umsg` passed
77unsafe extern "system" fn window_proc(hwnd: HWnd, umsg: WM32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
78    if umsg == WM::GETMINMAXINFO {
79        EARLY.set(hwnd, "early").unwrap();
80        LATE .set(hwnd, "late" ).unwrap();
81    }
82
83    eprintln!("window_proc({hwnd:?}, {umsg:?}");
84    eprintln!("  early: {:?}", EARLY.get_copy(hwnd));
85    eprintln!("  late:  {:?}", LATE .get_copy(hwnd));
86
87    match umsg {
88        WM::LBUTTONDOWN => {
89            // This blocks, without preventing `hwnd` from being closed, allowing me
90            // to experiment with yanking HWNDs out from under recursive wndprocs.
91            unsafe { MessageBoxA(null_mut(), "Message Box\0".as_ptr().cast(), "Caption\0".as_ptr().cast(), MB_OK) };
92            0
93        },
94        WM::RBUTTONDOWN => {
95            unsafe { destroy_window(hwnd) }.unwrap();
96            assert_eq!(ERROR::INVALID_WINDOW_HANDLE, unsafe { destroy_window(hwnd) }.unwrap_err());
97            0
98        },
99        WM::DESTROY => {
100            assert!(is_window(hwnd));
101            assert_eq!(ERROR::INVALID_WINDOW_HANDLE, EARLY.set(hwnd, "early").unwrap_err());    // unable to set "early" data: window is being destroyed
102            LATE.set(hwnd, "late").unwrap();                                                    // still able to set "late" data
103
104            static DESTROY : AtomicBool = AtomicBool::new(false);
105            if !DESTROY.load(Relaxed) {
106                DESTROY.store(true, Relaxed);
107                unsafe { destroy_window(hwnd) }.unwrap();
108                assert_eq!(ERROR::INVALID_WINDOW_HANDLE, LATE.set(hwnd, "late").unwrap_err());  // no longer able to set even "late" data: destroy_window returned
109            }
110            post_quit_message(0);
111            0
112        },
113        _ => unsafe { def_window_proc_w(hwnd, umsg, wparam, lparam) },
114    }
115}
116
117static EARLY : assoc::local::Slot<&'static str> = assoc::local::Slot::new_drop_early();
118static LATE  : assoc::local::Slot<&'static str> = assoc::local::Slot::new_drop_late();