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

0 winapi rust

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

\n

我从windows-rs 示例行 357获取了 Rust 应用程序状态部分,并对其进行了一些修改以适用于我的代码。唯一的问题是,它没有按预期工作......

\n

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

\n

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

\n

我该如何解决这个问题?

\n
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {\n    unsafe {\n        match message as u32 {\n            WM_NCCREATE => {\n                // ORIGINAL address given to CreateWindowExW: 0xb678ddf730\n                let cs = lparam as *const CREATESTRUCTW;\n                let state = (*cs).lpCreateParams as *mut State;\n                \n                println!("{:?}", state); // OUTPUT: 0xb678ddf730 (correct)\n\n                // After using the two functions:\n                SetWindowLongW(window, GWLP_USERDATA, state as i32);\n\n                let state = GetWindowLongW(window, GWLP_USERDATA) as *mut State;\n\n                println!("{:?}", state); // OUTPUT: 0x78ddf730 (missing first 2 digits "b6")\n                1\n            }\n            ...\n
Run Code Online (Sandbox Code Playgroud)\n

完整代码:

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

货物.toml:

\n
...\n\n[dependencies.windows-sys]\nversion = "0.36.1"\nfeatures = [\n    "Win32_Foundation",\n    "Win32_System_LibraryLoader",\n    "Win32_Graphics_Gdi",\n    "Win32_Graphics_Direct2D",\n    "Win32_UI_WindowsAndMessaging",\n]\n
Run Code Online (Sandbox Code Playgroud)\n

IIn*_*ble 5

和APIGetWindowLongW调用SetWindowLongW只能存储 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
Run Code Online (Sandbox Code Playgroud)

SetWindowLongPtrW(window, GWLP_USERDATA, state as isize);
Run Code Online (Sandbox Code Playgroud)

GetWindowLongW只需要将呼叫替换为呼叫即可GetWindowLongPtrW。此处不需要进行其他更改。

请注意,虽然在针对 64 位体系结构时,-Ptr可以通过windowswindows-sys包立即使用这些变体,但对于 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 _
}
Run Code Online (Sandbox Code Playgroud)

有了它,您就可以简单地调用GetWindowLongPtrW/SetWindowLongPtrW而不考虑目标体系结构,就像使用 Windows SDK 标头编写 C/C++ 代码一样。