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
GetWindowLongPtrWtwice? The second one could be simplylet ptr = ptr as *mut Box<Counter>;(that's probably not what causes the problem, but it would make sure thatGetWindowLongPtrWdoesn't return a different value the second time…)Boxat this point? It should belet ptr = ptr as *mut Counter;since callingBox::leakremoved theBoxand left a bare reference.GetWindowLongPtrWalways returns a same pointer. TheBoxis required here, if I remove theBoxin ` GetWindowLongPtrW(parent, GWLP_USERDATA) as *mut Box<Counter>;`, I cloud not get the initial value 1.Boxto get the initial value 1, then that means you probably really needlet ptr = *(ptr as *const *mut Counter);(I don't knowGetWindowLongPtrW, or really any of the Windows API, but casting to aBoxhere is just plain wrong since the value you passed toCreateWindowExWis not aBox, so there's no reason for Windows to give you one back).