为什么 SetWindowLongW 和 GetWindowLongW 会去除指针的前 2 位数字?



我正在尝试使应用程序状态在Rust中工作。

我从windows-rs样例第357行获得了Rust应用程序状态部分,并对其进行了一些修改,以便与我的代码一起工作。唯一的问题是,它没有像预期的那样工作……

从下面代码的注释中可以看到,我的问题是,在使用上述两个函数后,我的指针(state)丢失了前两位数字。

每次我运行这个,前两位数字总是丢失。

我该如何解决这个问题?

extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
unsafe {
match message as u32 {
WM_NCCREATE => {
// ORIGINAL address given to CreateWindowExW: 0xb678ddf730
let cs = lparam as *const CREATESTRUCTW;
let state = (*cs).lpCreateParams as *mut State;

println!("{:?}", state); // OUTPUT: 0xb678ddf730 (correct)
// After using the two functions:
SetWindowLongW(window, GWLP_USERDATA, state as i32);
let state = GetWindowLongW(window, GWLP_USERDATA) as *mut State;
println!("{:?}", state); // OUTPUT: 0x78ddf730 (missing first 2 digits "b6")
1
}
...

完整代码:

#![allow(dead_code)]
//#![allow(unused_)]
#![allow(unused_variables)]
use windows_sys::{
Win32::Foundation::*,
Win32::Graphics::Gdi::ValidateRect,
Win32::System::LibraryLoader::GetModuleHandleW,
Win32::UI::WindowsAndMessaging::*,
};
fn u16_str(string: &str) -> Vec<u16> {
let mut result: Vec<u16> = string.encode_utf16().collect();
result.push(0);
result
}
#[derive(Debug)]
struct State {
name: String,
}
fn main() {
unsafe {
let instance = GetModuleHandleW(std::ptr::null());
let wc = WNDCLASSEXW {
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
hCursor: LoadCursorW(0, IDC_ARROW),
hInstance: instance,
lpszClassName: u16_str("asd").as_ptr(),
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(wndproc),
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: 0,
hbrBackground: 0,
lpszMenuName: std::ptr::null(),
hIconSm: 0,
};
RegisterClassExW(&wc);
let mut state = State {
name: String::from("name"),
};
println!("{:?}", &mut state as *mut _);
CreateWindowExW(
0,
u16_str("asd").as_ptr(),
u16_str("Ablak 1 áéőüűö").as_ptr(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0, 0, instance, &mut state as *mut _ as _,
);
let mut message = std::mem::zeroed();
while GetMessageW(&mut message, 0, 0, 0) != 0 {
DispatchMessageW(&message);
}
}
}
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
unsafe {
match message as u32 {
WM_NCCREATE => {
let cs = lparam as *const CREATESTRUCTW;
let state = (*cs).lpCreateParams as *mut State;
println!("{:?}", state);
SetWindowLongW(window, GWLP_USERDATA, state as i32);
let state = GetWindowLongW(window, GWLP_USERDATA) as *mut State;
println!("{:?}", state);
1
}
WM_PAINT => {
//let state = GetWindowLongPtrW(window, GWLP_USERDATA) as *mut State;
//println!("{:?}", state);
//println!("{}", GetLastError());
println!("paint");
ValidateRect(window, std::ptr::null());
0
}
WM_DESTROY => {
println!("WM_DESTROY");
PostQuitMessage(0);
0
}
_ => DefWindowProcW(window, message, wparam, lparam),
}
}
}

Cargo.toml:

...
[dependencies.windows-sys]
version = "0.36.1"
features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",
"Win32_Graphics_Gdi",
"Win32_Graphics_Direct2D",
"Win32_UI_WindowsAndMessaging",
]

GetWindowLongWSetWindowLongW的API调用只能存储32位的值。32位是32位体系结构中指针的大小。由于您的目标是64位体系结构,因此必须遵循文档中的建议:

如果您正在检索指针或句柄,则此函数已被GetWindowLongPtrW函数所取代。(指针和句柄在32位Windows上是32位,在64位Windows上是64位。)要编写与32位和64位版本的Windows兼容的代码,请使用GetWindowLongPtrW

此函数已被SetWindowLongPtr函数取代。要编写兼容32位和64位Windows版本的代码,请使用SetWindowLongPtr函数。

你必须替换

SetWindowLongW(window, GWLP_USERDATA, state as i32);
//                                          ^^^^^^ truncates pointer to 32 bits

SetWindowLongPtrW(window, GWLP_USERDATA, state as isize);

GetWindowLongW调用只需要替换为GetWindowLongPtrW调用。此处不需要进行其他更改。

请注意,虽然-Ptr变体在针对64位体系结构时可以通过windowswindows-syscrate立即获得,但对于32位目标来说,它们是缺失的(参见这个GitHub问题)。您可以提供您自己的实现,例如

#[allow(non_snake_case)]
#[cfg(target_pointer_width = "32")]
unsafe fn SetWindowLongPtrW(window: HWND, index: WINDOW_LONG_PTR_INDEX, value: isize) -> isize {
SetWindowLongW(window, index, value as _) as _
}
#[allow(non_snake_case)]
#[cfg(target_pointer_width = "32")]
unsafe fn GetWindowLongPtrW(window: HWND, index: WINDOW_LONG_PTR_INDEX) -> isize {
GetWindowLongA(window, index) as _
}

有了这个,你可以简单地调用GetWindowLongPtrW/SetWindowLongPtrW,而不管目标架构是什么,就像使用Windows SDK头编写C/c++代码一样。

最新更新