I'm trying to use the WPARAM parameter in wndproc to get keyboard inputs using the "windows" crate. I found a github repo that tries to achieve the same thing (link to the specific code here) and they've managed to cast a WPARAM to an i32. When I try to do the exact same thing, I get the error Non-primitive cast: `WPARAM` as `i32` [E0605]. Since the repo I found is pretty old I imagine there's been changes made to the crate and I'm not really sure what I should be doing differently. Here's a snippet of my code if it helps:
extern "system" fn wnd_proc(window: HWND, message: u32, w_param: WPARAM, l_param: LPARAM)
-> LRESULT {
unsafe {
match message {
...
WM_SYSKEYDOWN => {
let vk_code: i32 = w_param as i32;
}
...
}
}
}
TL;DR: Replace
w_param as i32withw_param.0 as i32.The
windowscrate makes liberal use of the new type idiom. This is a powerful tool leveraging the type system to guard against misinterpreting data of identical binary types. It does this by inventing wrapper types whose sole purpose is to capture and persist semantic information.This is incredibly useful in general. It opens the opportunity to turn what would have been a run-time error into a compile-time error. For example, you cannot pass the
HFONTreturned fromCreateFontWintoCloseHandle. WhileHFONTs andHANDLEs essentially are justisizevalues at the binary interface, the new type wrappers prevent Rust code from confusing one for the other.This is how the
WPARAMwrapper is defined in thewindowscrate:This is a tuple struct with a single field that can be accessed by index using the
.0syntax. The effect of the#[repr(transparent)]attribute is that theWPARAMstruct and its contained field have the same binary layout. This is crucial so thatWPARAMcan appear in an FFI in place of ausize. After all, thewnd_procabove is called by the system (written in C and C++) that doesn't (need to) know what a tuple struct is.All due praises aside, the new type idiom doesn't provide much value here. The
WPARAM(usize) andLPARAM(isize) values passed to the window procedure are essentially type-erased. Their meaning has to be recovered on a message-by-message basis, with frequent wrapping and unwrapping in between. To illustrate this, here is how aWM_SYSKEYDOWNhandler can be implemented:This may seem overly verbose, but that is in part due to re-encoding the (
u16) value inside aVIRTUAL_KEYwrapper. That can be trimmed down to a one-liner, e.g.,let vk = VIRTUAL_KEY(w_param.0 as _);, leaving us with a sanity-checked new type that cannot accidentally get confused for an arbitraryu16value.With this, we've come full circle: While the new type idiom doesn't provide much value to the
WPARAMwrapper, it sure does once theusizepayload has been repacked into aVIRTUAL_KEYstruct, unleashing the new type idiom's full potential again.A note on why the code you've found on GitHub works without the added verbosity: It uses the
winapicrate that models ABI types as type aliases. A type alias merely introduces a new name for an existing type.WPARAMis thus an alternative spelling for theusizetype.This is very similar to what the
windows-syscrate does: ItsWPARAMtype alias is identical to that of thewinapicrate.Neither of these low-level binding crates makes much use of Rust's type system beyond the ABI guarantees.