How to pass an unsafe extern "system" fn


#1

I’m trying to do some win32 GUI programming with rust’s win32 APIs.
I want to try to create some generic functions for creating windows.
The problem is that some windows will need a custom handler for messages so I need to be able to pass this (or the default handler).
Here’s some of the code:

// Application

quick_main!(run);

fn run() -> Result<()> {
    let mut window = wui::create_main_window("MainWindow", "Checksum",
                                             &handle_main_event)?;
    Ok(wui::event_loop(&mut window)?) // for error_chain
}

unsafe extern "system"
fn handle_main_event(hwnd: HWND, msg: UINT, wparam: WPARAM,
                     lparam: LPARAM) -> LRESULT {
    match msg {
        // TODO
        _ => DefWindowProcW(hwnd, msg, wparam, lparam)
    }
}

// wui crate
pub struct Window {
    pub hwnd: HWND,
}

pub type WindowProc = unsafe extern "system" fn(HWND, UINT, WPARAM,
                                                LPARAM)->LRESULT; // or Fn ?

pub fn as_ws(s: &str) -> Vec<u16> {
    OsStr::new(s).encode_wide().chain(once(0)).collect()
}

pub fn event_loop(window: &mut Window) -> Result<()> {
    loop {
        match util::handle_event(window) {
            Ok(false) => return Ok(()), // User quit
            Err(err) => bail!(err),
            _ => () // continue
        }
    }
}

pub fn create_main_window(class_name: &str, title: &str,
                          window_proc: &WindowProc) -> Result<Window> {
    util::create_window(class_name, title, true, *window_proc)
}

The error I’m getting is this:

R:\rs\Checksum>cargo run --release --
   Compiling Checksum v1.0.0 (file:///R:/rs/Checksum)
error[E0308]: mismatched types
  --> src\main.rs:27:46
   |
27 |                                              &handle_main_event)?;
   |                                              ^^^^^^^^^^^^^^^^^^ expected fn pointer,
found fn item
   |
   = note: expected type `&unsafe extern "system" fn(*mut winapi::shared::windef::HWND__, u32, usize, isize) -> isize`
              found type `&unsafe extern "system" fn(*mut winapi::shared::windef::HWND__, u32, usize, isize) -> isize {handle_main_event}`

error: aborting due to previous error

I am guessing that I need to use Fn but unfortunately simply changing fn to Fn doesn’t work.


#2

You just need something like:

type MyFn = unsafe extern "system" fn(hwnd: HWND, ...);

 let mut window = wui::create_main_window("MainWindow", "Checksum",
                                             handle_main_event as MyFn)?;

#3

Thanks! Rust told me to write it like this: &(handle_main_event as WindowProc) which worked:-)


#4

Oh, you already had the WindowProc type alias - sorry, I missed it. In fact, I didn’t scroll down far enough in your snippet :slight_smile:.

pub fn create_main_window(class_name: &str, title: &str,
                         window_proc: &WindowProc)

window_proc can be just WindowProc, you don’t need the reference (which is why I suggested the syntax before, but I didn’t see this fn that you had.

The important note here is that an “fn item” is a function “pointer” to a specific function, and that’s different from a general fn pointer. An “fn item”, since it refers to a specific function, is a ZST - you can think of it almost like an alias for the underlying fn. A fn pointer, however, is a dynamic thing - it’s actually a pointer and has its size.


#5

Thank you! That’s simplified the code a lot. Now I just pass plain window_proc, don’t need & or *; much nicer.