How to retrieve a struct in window_proc function in WM_COMMAND msg?

58 views Asked by At

I'm trying to write a simple windows app in rust with windows-rs crate. I'd like to have a state wrapped in Counter struct and pass it to window_proc function. It is successful to retrieve the pointer and store it by GWLP_USERDATA index. My question is that why it increases in WM_CREATE msg without error, but does not work in WM_COMMAND msg?

struct Counter {
    counter: i32
}

fn main() -> Result<()> {
    let lparam: *mut Counter = Box::leak(Box::new(Counter{
        counter: 1
    }));

    unsafe {
        let mut instance = Default::default();
        GetModuleHandleExW(0, None, &mut instance)?;

        let wnd_class = WNDCLASSEXW {
           // ... ...
        };

        let atom = RegisterClassExW(&wnd_class);
        debug_assert!(atom != 0);

        let hwnd = CreateWindowExW(
            // ... ...
            Some(lparam as *mut c_void),
        );

        // ... ...
    }
}

fn increase_and_print(parent: HWND) {
    unsafe {
        let ptr = GetWindowLongPtrW(parent, GWLP_USERDATA);
        println!("GetWindowLongPtrW: {}", ptr);

        let ptr = GetWindowLongPtrW(parent, GWLP_USERDATA) as *mut Box<Counter>;
        debug_assert!(!ptr.is_null());
        let c =  &mut *ptr;
        c.counter += 1;
        println!("counter: {}", c.counter);
    }
}

extern "system" fn window_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
    unsafe {
        match msg {
            WM_CREATE => {

                // Create a click button
        
                // Retrieve the pointer to your state and store it in the window's user data
                SetWindowLongPtrW(hwnd, GWLP_USERDATA, lparam.0 as isize);
                increase_and_print(hwnd);

                LRESULT(0)
            },
            WM_DESTROY => {
                PostQuitMessage(0);
                LRESULT(0)
            },
            WM_COMMAND => {
                increase_and_print(hwnd);
                return LRESULT(0);
            },
            _ => DefWindowProcW(hwnd, msg, wparam, lparam)
        }
    }
}

The counter is initialized to 1, then increased to 2 in WM_CREATE msg. I expect it prints 3 after clicking the button (i.e. WM_COMMAND msg was called) Output in console

GetWindowLongPtrW: 359815702944
counter: 2
GetWindowLongPtrW: 359815702944
thread 'main' panicked at 'misaligned pointer dereference: address must be a multiple of 0x4 but is 0xb', apps\counter\src\main.rs:73:9

Complete code

1

There are 1 answers

1
chengbin du On

I search for apps created by windows-rs on GitHub, finally I got this robmikh/minesweeper-rs windows.rs. It's totally what I was looking for.

Set counter to User data

let cs = lparam.0 as *const CREATESTRUCTW;
let counter = (*cs).lpCreateParams as *mut Counter;
SetWindowLongPtrW(hwnd, GWLP_USERDATA, counter as _);

Retrieve counter and update its value

unsafe {
    let counter_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut Counter;
    if let Some(c) = counter_ptr.as_mut() {
        c.counter += 1;
        println!("counter: {}", c.counter);
    }
}

As I am new to Rust, I would be grateful if somebody could explain this in detail.