windowswinapirust

windows-rs SetWindowLongPtrW failed, error code 0


Goal

To be able to access my struct's fields in the window_proc function.

Problem

I have narrowed down my error to the following line of code below. This results in the error below the code. I think I am not setting the pointer correctly. But I am not sure what the proper way to do this is.

// Save the Floppa instance to the user data pointer so it can be accessed
// from the window_proc.
let floppa_ptr: *mut Floppa = Box::into_raw(Box::new(self));
if SetWindowLongPtrW(hwnd, GWLP_USERDATA, floppa_ptr as isize) == 0 {
    Err(format!("Failed to set the user data pointer: {}", GetLastError().0))?;
}
thread 'main' panicked at src/main.rs:205:23:
called `Result::unwrap()` on an `Err` value: "Failed to set the user data pointer: 0"
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\glorp.exe` (exit code: 101)

Here is my struct definition if it helps.

#[derive(Debug)]
struct Floppa {
    bitmap_handle: HGDIOBJ,
    width: i32,
    height: i32,
    alpha: i32
}

Solution

  • Everything is working as documented. This is what the documentation for SetWindowLongPtrW has to say:

    Return value

    If the function succeeds, the return value is the previous value of the specified offset.

    If the previous value is zero and the function succeeds, the return value is zero, but the function does not clear the last error information. To determine success or failure, clear the last error information by calling SetLastError with 0, then call SetWindowLongPtr. Function failure will be indicated by a return value of zero and a GetLastError result that is nonzero.

    The relevant nIndex value is documented as:

    GWLP_USERDATA: Sets the user data associated with the window. This data is intended for use by the application that created the window. Its value is initially zero.

    The issue is thus the incorrect error discovery code. It needs to be changed to this:

    let floppa_ptr: *mut Floppa = Box::into_raw(Box::new(self));
    SetLastError(ERROR_SUCCESS);
    if SetWindowLongPtrW(hwnd, GWLP_USERDATA, floppa_ptr as isize) == 0 &&
       GetLastError() != ERROR_SUCCESS {
        Err(format!("Failed to set the user data pointer: {}", GetLastError().0))?;
    }
    

    Keep in mind that the ownership semantics of the data at index GWLP_USERDATA in the window-private memory are wildly unclear. If you are registering a custom window class you should consider allocating additional per-window memory using the cbWndExtra field in the WNDCLASSEXW structure.